PAGE 118,121
TITLE DSKETTE -- 06/10/85  DISKETTE BIOS
.286C
.XLIST
INCLUDE DSEG.INC
INCLUDE POSTEQU.INC
.LIST
CODE	SEGMENT BYTE PUBLIC

	PUBLIC	DISK_INT_1
	PUBLIC	SEEK
	PUBLIC	DSKETTE_SETUP
	PUBLIC	DISKETTE_IO_1

	EXTRN	CMOS_READ:NEAR		; READ CMOS LOCATION ROUTINE
	EXTRN	DDS:NEAR		; LOAD (DS) WITH DATA SEGMENT SELECTOR
	EXTRN	DISK_BASE:NEAR		; DISKETTE PARAMETER TABLE LOCATION
	EXTRN	WAITF:NEAR		; FIXED WAIT ROUTINE - (CX)*15.086 US

;-- INT  13H -------------------------------------------------------------------
; DISKETTE I/O
;	THIS INTERFACE PROVIDES ACCESS TO THE 5 1/4" DISKETTE DRIVES
;	320/360K DISKETTE DRIVES AND 1.2M DISKETTE DRIVES SUPPORTED
; INPUT
;	(AH)= 00H  RESET DISKETTE SYSTEM
;		  HARD RESET TO NEC. PREPARE COMMAND, RECALIBRATE REQUIRED
;		  ON ALL DRIVES
;-------------------------------------------------------------------------------
;	(AH)= 01H  READ THE STATUS OF THE SYSTEM INTO (AH)
;		  @DSKETTE_STATUS FROM LAST OPERATION IS USED
;-------------------------------------------------------------------------------
;	REGISTERS FOR READ/WRITE/VERIFY/FORMAT
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;	(DH) - HEAD NUMBER (0-1 ALLOWED, NOT VALUE CHECKED)
;	(CH) - TRACK NUMBER (NOT VALUE CHECKED)
;		  MEDIA     DRIVE	 TRACK NUMBER
;		 320/360   320/360	     0-39
;		 320/360    1.2M	     0-39
;		 1.2M	    1.2M	     0-79
;		 720K	    720K	     0-79
;	(CL) - SECTOR NUMBER (NOT VALUE CHECKED, NOT USED FOR FORMAT)
;		  MEDIA     DRIVE	 SECTOR NUMBER
;		 320/360   320/360	      1-8/9
;		 320/360    1.2M	      1-8/9
;		  1.2M	    1.2M	      1-15
;		  720K	    720K	      1-9
;	(AL) - NUMBER OF SECTORS (NOT VALUE CHECKED)
;		  MEDIA     DRIVE	 MAX NUMBER OF SECTORS
;		 320/360   320/360		 8/9
;		 320/360    1.2M		 8/9
;		  1.2M	    1.2M		 15
;		  720K	    720K		 9
;
;	(ES:BX) - ADDRESS OF BUFFER ( REQUIRED FOR VERIFY)
;
;-------------------------------------------------------------------------------
;	(AH)= 02H  READ THE DESIRED SECTORS INTO MEMORY
;-------------------------------------------------------------------------------
;	(AH)= 03H  WRITE THE DESIRED SECTORS FROM MEMORY
;-------------------------------------------------------------------------------
;	(AH)= 04H  VERIFY THE DESIRED SECTORS
;-------------------------------------------------------------------------------
;	(AH)= 05H  FORMAT THE DESIRED TRACK
;		  FOR THE FORMAT OPERATION. THE BUFFER POINTER (ES,BX) MUST
;		  POINT TO THE COLLECTION OF DESIRED ADDRESS FIELDS FOR THE
;		  TRACK.  EACH FIELD IS COMPOSED OF 4 BYTES, (C,H,R,N), WHERE
;		  C = TRACK NUMBER, H=HEAD NUMBER, R = SECTOR NUMBER, N= NUMBER
;		  OF BYTES PER SECTOR (00=128, 01=256, 02=512. 03=1024,)
;		  THERE MUST BE ONE ENTRY FOR EVERY SECTOR ON THE TRACK.
;		  THIS INFORMATION IS USED TO FIND THE REQUESTED SECTOR DURING
;		  READ/WRITE ACCESS.
;		  PRIOR TO FORMATTING A DISKETTE, IF THERE EXISTS MORE THAN
;		  ONE SUPPORTED MEDIA FORMAT TYPE WITHIN THE DRIVE IN QUESTION,
;		  THEN "SET DASD TYPE" (INT 13H, AH = 17H) MUST BE CALLED TO
;		  SET THE DISKETTE TYPE THAT IS TO BE FORMATTED.  IF "SET DASD
;		  TYPE" IS NOT CALLED, THE FORMAT ROUTINE WILL ASSUME THE
;		  MEDIA FORMAT TO BE THE MAXIMUM CAPACITY OF THE DRIVE.
;		  IN ORDER TO FORMAT 320/360K MEDIA IN EITHER A 320/360K OR
;		  1.2M DISKETTE DRIVE THE GAP LENGTH FOR FORMAT PARAMETER
;		  OF DISK BASE MUST BE CHANGE TO 050H.	ALSO THE EOT
;		  PARAMETER (LAST SECTOR ON TRACK) MUST BE SET TO THE
;		  DESIRED NUMBER OF SECTORS/TRACK - 8 FOR 320K, 9 FOR 360K.
;		  DISK_BASE IS POINTED TO BY DISK POINTER LOCATED AT
;		  ABSOLUTE ADDRESS 0:78.
;		  WHEN 320/360K FORMAT OPERATIONS ARE COMPLETE, THE PARAMETERS
;		  SHOULD BE RESTORED TO THEIR RESPECTIVE INITIAL VALUES.
;-------------------------------------------------------------------------------
;	(AH)= 08H  READ DRIVE PARAMETERS
;	REGISTERS
;	  INPUT
;	    (DL) - DRIVE NUMBER (0-1 ALLOWED. VALUE CHECKED)
;	  OUTPUT
;	    (ES:DI) POINTS TO DISK BASE
;	    (CH) - LOW ORDER 8 OF 10 BITS MAXIMUM NUMBER OF TRACKS
;	    (CL) - BITS 7 & 6 - HIGH ORDER TWO BITS OF MAXIMUM TRACKS
;		   BITS 5 THRU 0 - MAXIMUM SECTORS PER TRACK
;	    (DH) - MAXIMUM HEAD NUMBER
;	    (DL) - NUMBER OF DISKETTE DRIVES INSTALLED
;	    (BH) - 0
;	    (BL) - BITS 7 THRU 4 - 0
;		   BITS 3 THRU 0 - VALID DRIVE TYPE VALUE IN CMOS
;	    (AX) - 0
;	   UNDER THE FOLLOWING CIRCUMSTANCES:
;	    (1) THE DRIVE NUMBER IS INVALID,
;	    (2) THE DRIVE TYPE IS UNKNOWN AND CMOS IS NOT PRESENT,
;	    (3) THE DRIVE TYPE IS UNKNOWN AND CMOS IS BAD,
;	    (4) OR THE DRIVE TYPE IS UNKNOWN AND THE CMOS DRIVE TYPE IS INVALID
;	    THEN ES,AX,BX,CX,DH,DI=0 ; DL=NUMBER OF DRIVES.
;	    IF NO DRIVES ARE PRESENT THEN: ES,AX,BX,CX,DX,DI=0.
;	    @DSKETTE_STATUS = 0 AND CY IS RESET.
PAGE
;-------------------------------------------------------------------------------
;	(AH)= 15H  READ DASD TYPE
;	REGISTERS
;	(AH) - ON RETURN IF CARRY FLAG NOT SET, OTHERWISE ERROR
;		 00 - DRIVE NOT PRESENT
;		 01 - DISKETTE, NO CHANGE LINE AVAILABLE
;		 02 - DISKETTE, CHANGE LINE AVAILABLE
;		 03 - FIXED DISK
;	(DL) - DRIVE NUMBER (0-1 ALLOWED. VALUE CHECKED)
;
;-------------------------------------------------------------------------------
;	(AH)= 16H  DISK CHANGE LINE STATUS
;	REGISTERS
;	(AH)=00 - DISK CHANGE LINE NOT ACTIVE
;	     06 - DISK CHANGE LINE ACTIVE & CARRY BIT ON
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED)
;
;-------------------------------------------------------------------------------
;	(AH)= 17H  SET DASD TYPE FOR FORMAT
;	REGISTERS
;	(AL) -	 00 - NOT USED
;		 01 - DISKETTE 320/360K IN 360K DRIVE
;		 02 - DISKETTE 360K IN 1.2M DRIVE
;		 03 - DISKETTE 1.2M IN 1.2M DRIVE
;		 04 - DISKETTE 720K IN 720K DRIVE
;	(DL) - DRIVE NUMBER (0-1 ALLOWED, VALUE CHECKED;
;		DO NOT USE WHEN DISKETTE ATTACH CARD USED)
;-------------------------------------------------------------------------------
;	DISK CHANGE STATUS IS ONLY CHECKED WHEN A 1.2M BYTE DISKETTE
;	DRIVE IS SPECIFIED.  IF THE DISK CHANGE LINE IS FOUND TO BE
;	ACTIVE THE FOLLOWING ACTIONS TAKE PLACE:
;		ATTEMPT TO RESET DISK CHANGE LINE TO INACTIVE STATE.
;		IF ATTEMPT SUCCEEDS SET DASD TYPE FOR FORMAT AND RETURN DISK
;		CHANGE ERROR CODE
;		IF ATTEMPT FAILS RETURN TIMEOUT ERROR CODE AND SET DASD TYPE
;		TO A PREDETERMINED STATE INDICATING MEDIA TYPE UNKNOWN.
;	IF THE DISK CHANGE LINE IN INACTIVE PERFORM SET DASD TYPE FOR FORMAT.
;
; DATA VARIABLE -- @DISK_POINTER
;	DOUBLE WORD POINTER TO THE CURRENT SET OF DISKETTE PARAMETERS
;-------------------------------------------------------------------------------
; OUTPUT FOR ALL FUNCTIONS
;	AH = STATUS OF OPERATION
;		STATUS BITS ARE DEFINED IN THE EQUATES FOR @DSKETTE_STATUS
;		VARIABLE IN THE DATA SEGMENT OF THIS MODULE
;	CY = 0	SUCCESSFUL OPERATION (AH=0 ON RETURN, EXCEPT FOR READ DASD
;		TYPE AH=(15)).
;	CY = 1	FAILED OPERATION (AH HAS ERROR REASON)
;	FOR READ/WRITE/VERIFY
;		DS,BX,DX,CX PRESERVED
;	NOTE: IF AN ERROR IS REPORTED BY THE DISKETTE CODE, THE APPROPRIATE
;		ACTION IS TO RESET THE DISKETTE, THEN RETRY THE OPERATION.
;		ON READ ACCESSES, NO MOTOR START DELAY IS TAKEN, SO THAT
;		THREE RETRIES ARE REQUIRED ON READS TO ENSURE THAT THE
;		PROBLEM IS NOT DUE TO MOTOR START-UP.
;-------------------------------------------------------------------------------
; DISKETTE STATE MACHINE - ABSOLUTE ADDRESS 40:90 (DRIVE A) & 91 (DRIVE B)
;   -----------------------------------------------------------------
;   |	    |	    |	    |	    |	    |	    |	    |	    |
;   |	7   |	6   |	5   |	4   |	3   |	2   |	1   |	0   |
;   |	    |	    |	    |	    |	    |	    |	    |	    |
;   -----------------------------------------------------------------
;	|	|	|	|	|	|	|	|
;	|	|	|	|	|	-----------------
;	|	|	|	|	|		|
;	|	|	|	|    RESERVED		|
;	|	|	|	|		  PRESENT STATE
;	|	|	|	|	000: 360K IN 360K DRIVE UNESTABLISHED
;	|	|	|	|	001: 360K IN 1.2M DRIVE UNESTABLISHED
;	|	|	|	|	010: 1.2M IN 1.2M DRIVE UNESTABLISHED
;	|	|	|	|	011: 360K IN 360K DRIVE ESTABLISHED
;	|	|	|	|	100: 360K IN 1.2M DRIVE ESTABLISHED
;	|	|	|	|	101: 1.2M IN 1.2M DRIVE ESTABLISHED
;	|	|	|	|	110: RESERVED
;	|	|	|	|	111: NONE OF THE ABOVE
;	|	|	|	|
;	|	|	|	------> MEDIA/DRIVE ESTABLISHED
;	|	|	|
;	|	|	--------------> DOUBLE STEPPING REQUIRED (360K IN 1.2M
;	|	|			 DRIVE)
;	|	|
;	------------------------------> DATA TRANSFER RATE FOR THIS DRIVE:
;
;						00: 500 KBS
;						01: 300 KBS
;						10: 250 KBS
;						11: RESERVED
;
;-------------------------------------------------------------------------------
; STATE OPERATION STARTED - ABSOLUTE ADDRESS 40:92 (DRIVE A) & 93 (DRIVE B)
;-------------------------------------------------------------------------------
; PRESENT CYLINDER NUMBER - ABSOLUTE ADDRESS 40:94 (DRIVE A) & 95 (DRIVE B)
;-------------------------------------------------------------------------------
PAGE
	ASSUME	CS:CODE,DS:DATA,ES:DATA
DISKETTE_IO_1	PROC	FAR		;>>> ENTRY POINT FOR ORG 0EC59H
	STI				; INTERRUPTS BACK ON
	PUSH	BP			; USER REGISTER
	PUSH	DI			; USER REGISTER
	PUSH	DX			; HEAD #, DRIVE # OR USER REGISTER
	PUSH	BX			; BUFFER OFFSET PARAMETER OR REGISTER
	PUSH	CX			; TRACK #-SECTOR # OR USER REGISTER
	MOV	BP,SP			; BP	 => PARAMETER LIST DEP. ON AH
					; [BP]	 = SECTOR #
					; [BP+1] = TRACK #
					; [BP+2] = BUFFER OFFSET
					; FOR RETURN OF DRIVE PARAMETERS:
					; CL/[BP] = BITS 7&6 HI BITS OF MAX CYL
					;	    BITS 0-5 MAX SECTORS/TRACK
					; CH/[BP+1] = LOW 8 BITS OF MAX CYL.
					; BL/[BP+2] = BITS 7-4 = 0
					;	      BITS 3-0 = VALID CMOS TYPE
					; BH/[BP+3] = 0
					; DL/[BP+4] = # DRIVES INSTALLED
					; DH/[BP+5] = MAX HEAD #
					; DI/[BP+6] = OFFSET TO DISK BASE
	PUSH	DS			; BUFFER SEGMENT PARM OR USER REGISTER
	PUSH	SI			; USER REGISTERS
	CALL	DDS			; SEGMENT OF BIOS DATA AREA TO DS
	CMP	AH,(FNC_TAE-FNC_TAB)/2	; CHECK FOR > LARGEST FUNCTION
	JB	OK_FUNC 		; FUNCTION OK

	MOV	AH,14H			; REPLACE WITH KNOWN INVALID FUNCTION
OK_FUNC:
	CMP	AH,1			; RESET OR STATUS ?
	JBE	OK_DRV			; IF RESET OR STATUS DRIVE ALWAYS OK
	CMP	AH,8			; READ DRIVE PARMS ?
	JZ	OK_DRV			; IF SO DRIVE CHECKED LATER
	CMP	DL,1			; DRIVES 0 AND 1 OK
	JBE	OK_DRV			; IF 0 OR 1 THEN JUMP
	MOV	AH,14H			; REPLACE WITH KNOWN INVALID FUNCTION
OK_DRV:
	MOV	CL,AH			; CL =	FUNCTION
	XOR	CH,CH			; CX = FUNCTION
	SHL	CL,1			; FUNCTION TIMES 2
	MOV	BX,OFFSET FNC_TAB	; LOAD START OF FUNCTION TABLE
	ADD	BX,CX			; ADD OFFSET INTO TABLE => ROUTINE
	MOV	AH,DH			; AX = HEAD #,# OF SECTORS OR DASD TYPE
	XOR	DH,DH			; DX = DRIVE #
	MOV	SI,AX			; SI = HEAD #,# OF SECTORS OR DASD TYPE
	MOV	DI,DX			; DI = DRIVE #
	MOV	AH,@DSKETTE_STATUS	; LOAD STATUS TO AH FOR STATUS FUNCTION
	MOV	@DSKETTE_STATUS,0	; INITIALIZE FOR ALL OTHERS

;	THROUGHOUT THE DISKETTE BIOS, THE FOLLOWING INFORMATION IS CONTAINED IN
;	THE FOLLOWING MEMORY LOCATIONS AND REGISTERS. NOT ALL DISKETTE BIOS
;	FUNCTIONS REQUIRE ALL OF THESE PARAMETERS.
;
;		DI     : DRIVE #
;		SI-HI  : HEAD #
;		SI-LOW : # OF SECTORS OR DASD TYPE FOR FORMAT
;		ES     : BUFFER SEGMENT
;		[BP]   : SECTOR #
;		[BP+1] : TRACK #
;		[BP+2] : BUFFER OFFSET
;
;	ACROSS CALLS TO SUBROUTINES THE CARRY FLAG (CY=1), WHERE INDICATED IN
;	SUBROUTINE PROLOGUES, REPRESENTS AN EXCEPTION RETURN (NORMALLY AN ERROR
;	CONDITION). IN MOST CASES, WHEN CY = 1, @DSKETTE_STATUS CONTAINS THE
;	SPECIFIC ERROR CODE.
					; (AH) = @DSKETTE_STATUS
	CALL	WORD PTR CS:[BX]	; CALL THE REQUESTED FUNCTION
	POP	SI			; RESTORE ALL REGISTERS
	POP	DS
	POP	CX
	POP	BX
	POP	DX
	POP	DI
	POP	BP
	RET	2			; THROW AWAY SAVED FLAGS

;-------------------------------------------------------------------------------
FNC_TAB DW	DISK_RESET		; AH = 00; RESET
	DW	DISK_STATUS		; AH = 01; STATUS
	DW	DISK_READ		; AH = 02; READ
	DW	DISK_WRITE		; AH = 03; WRITE
	DW	DISK_VERF		; AH = 04; VERIFY
	DW	DISK_FORMAT		; AH = 05; FORMAT
	DW	FNC_ERR 		; AH = 06; INVALID
	DW	FNC_ERR 		; AH = 07; INVALID
	DW	DISK_PARMS		; AH = 08; READ DRIVE PARAMETERS
	DW	FNC_ERR 		; AH = 09; INVALID
	DW	FNC_ERR 		; AH = 0A; INVALID
	DW	FNC_ERR 		; AH = 0B; INVALID
	DW	FNC_ERR 		; AH = 0C; INVALID
	DW	FNC_ERR 		; AH = 0D; INVALID
	DW	FNC_ERR 		; AH = 0E; INVALID
	DW	FNC_ERR 		; AH = 0F; INVALID
	DW	FNC_ERR 		; AH = 10; INVALID
	DW	FNC_ERR 		; AH = 11; INVALID
	DW	FNC_ERR 		; AH = 12; INVALID
	DW	FNC_ERR 		; AH = 13; INVALID
	DW	FNC_ERR 		; AH = 14; INVALID
	DW	DISK_TYPE		; AH = 15; READ DASD TYPE
	DW	DISK_CHANGE		; AH = 16; CHANGE STATUS
	DW	FORMAT_SET		; AH = 17; SET DASD TYPE
FNC_TAE EQU	$			; END

DISKETTE_IO_1	ENDP
PAGE
;-------------------------------------------------------------------------------
; DISK_RESET:	RESET THE DISKETTE SYSTEM
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
DISK_RESET	PROC	NEAR
	MOV	DX,03F2H		; ADAPTER CONTROL PORT
	CLI				; NO INTERRUPTS
	MOV	AL,@MOTOR_STATUS	; GET DIGITAL OUTPUT REGISTER REFLECTION
	AND	AL,00111111B		; KEEP SELECTED AND MOTOR ON BITS
	ROL	AL,4			; MOTOR VALUE TO HIGH NIBBLE
					; DRIVE SELECT TO LOW NIBBLE
	OR	AL,00001000B		; TURN ON INTERRUPT ENABLE
	OUT	DX,AL			; RESET THE ADAPTER
	MOV	@SEEK_STATUS,0		; SET RECALIBRATE REQUIRED ON ALL DRIVES
	JMP	$+2			; WAIT FOR I/O
	OR	AL,00000100B		; TURN OFF RESET BIT
	OUT	DX,AL			; RESET THE ADAPTER
	STI				; ENABLE THE INTERRUPTS
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	DR_ERR			; IF ERROR, RETURN IT
	MOV	CX,11000000B		; CL = EXPECTED @NEC_STATUS

NXT_DRV:
	PUSH	CX			; SAVE FOR CALL
	MOV	AX,OFFSET DR_POP_ERR	; LOAD NEC_OUTPUT ERROR ADDRESS
	PUSH	AX			; "
	MOV	AH,08H			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	POP	AX			; THROW AWAY ERROR RETURN
	CALL	RESULTS 		; READ IN THE RESULTS
	POP	CX			; RESTORE AFTER CALL
	JC	DR_ERR			; ERROR RETURN
	CMP	CL,@NEC_STATUS		; TEST FOR DRIVE READY TRANSITION
	JNZ	DR_ERR			; EVERYTHING OK
	INC	CL			; NEXT EXPECTED @NEC_STATUS
	CMP	CL,11000011B		; ALL POSSIBLE DRIVES CLEARED
	JBE	NXT_DRV 		; FALL THRU IF 11000100B OR >

;-----	SEND SPECIFY COMMAND TO NEC

	MOV	AX,OFFSET RESBAC	; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	AH,03H			; SPECIFY COMMAND
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	SUB	DL,DL			; FIRST SPECIFY BYTE
	CALL	GET_PARM		; GET PARAMETER TO AH
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	MOV	DL,1			; SECOND SPECIFY BYTE
	CALL	GET_PARM		; GET PARAMETER TO AH
	CALL	NEC_OUTPUT		; OUTPUT THE COMMAND
	POP	AX			; POP ERROR RETURN
RESBAC:
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET

DR_POP_ERR:
	POP	CX			; CLEAR STACK
DR_ERR:
	OR	@DSKETTE_STATUS,BAD_NEC ; SET ERROR CODE
	JMP	SHORT RESBAC		; RETURN FROM RESET
DISK_RESET	ENDP

;-------------------------------------------------------------------------------
; DISK_STATUS:	DISKETTE STATUS.
;
; ON ENTRY:	AH : STATUS OF PREVIOUS OPERATION
;
; ON EXIT:	AH, @DSKETTE_STATUS, CY REFLECT STATUS OF PREVIOUS OPERATION.
;-------------------------------------------------------------------------------
DISK_STATUS	PROC	NEAR
	MOV	@DSKETTE_STATUS,AH	; PUT BACK FOR SETUP_END
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET
DISK_STATUS	ENDP
;-------------------------------------------------------------------------------
; DISK_READ:	DISKETTE READ.
;
; ON ENTRY:	DI     : DRIVE #
;		SI-HI  : HEAD #
;		SI-LOW : # OF SECTORS
;		ES     : BUFFER SEGMENT
;		[BP]   : SECTOR #
;		[BP+1] : TRACK #
;		[BP+2] : BUFFER OFFSET
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
DISK_READ	PROC	NEAR
	AND	@MOTOR_STATUS,01111111B ; INDICATE A READ OPERATION
	MOV	AX,0E646H		; AX = NEC COMMAND, DMA COMMAND
	CALL	RD_WR_VF		; COMMON READ/WRITE/VERIFY
	RET
DISK_READ	ENDP
;-------------------------------------------------------------------------------
; DISK_WRITE:	DISKETTE WRITE.
;
; ON ENTRY:	DI     : DRIVE #
;		SI-HI  : HEAD #
;		SI-LOW : # OF SECTORS
;		ES     : BUFFER SEGMENT
;		[BP]   : SECTOR #
;		[BP+1] : TRACK #
;		[BP+2] : BUFFER OFFSET
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
DISK_WRITE	PROC	NEAR
	MOV	AX,0C54AH		; AX = NEC COMMAND, DMA COMMAND
	OR	@MOTOR_STATUS,10000000B ; INDICATE WRITE OPERATION
	CALL	RD_WR_VF		; COMMON READ/WRITE/VERIFY
	RET
DISK_WRITE	ENDP
;-------------------------------------------------------------------------------
; DISK_VERF:	DISKETTE VERIFY.
;
; ON ENTRY:	DI     : DRIVE #
;		SI-HI  : HEAD #
;		SI-LOW : # OF SECTORS
;		ES     : BUFFER SEGMENT
;		[BP]   : SECTOR #
;		[BP+1] : TRACK #
;		[BP+2] : BUFFER OFFSET
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
DISK_VERF	PROC	NEAR
	AND	@MOTOR_STATUS,01111111B ; INDICATE A READ OPERATION
	MOV	AX,0E642H		; AX = NEC COMMAND, DMA COMMAND
	CALL	RD_WR_VF		; COMMON READ/WRITE/VERIFY
	RET
DISK_VERF	ENDP
;-------------------------------------------------------------------------------
; DISK_FORMAT:	DISKETTE FORMAT.
;
; ON ENTRY:	DI     : DRIVE #
;		SI-HI  : HEAD #
;		SI-LOW : # OF SECTORS
;		ES     : BUFFER SEGMENT
;		[BP]   : SECTOR #
;		[BP+1] : TRACK #
;		[BP+2] : BUFFER OFFSET
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
DISK_FORMAT	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	CALL	FMT_INIT		; ESTABLISH STATE IF UNESTABLISHED
	OR	@MOTOR_STATUS,10000000B ; INDICATE WRITE OPERATION
	CALL	MED_CHANGE		; CHECK MEDIA CHANGE AND RESET IF SO
	JC	FM_DON			; MEDIA CHANGED, SKIP
	CALL	SEND_RATE		; SEND DATA RATE TO CONTROLLER
	MOV	AL,04AH 		; WILL WRITE TO THE DISKETTE
	CALL	DMA_SETUP		; SET UP THE DMA
	JC	FM_DON			; RETURN WITH ERROR
	MOV	AH,04DH 		; ESTABLISH THE FORMAT COMMAND
	CALL	NEC_INIT		; INITIALIZE THE NEC
	MOV	AX,OFFSET FM_DON	; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	DL,3			; BYTES/SECTOR VALUE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	MOV	DL,4			; SECTORS/TRACK VALUE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	MOV	DL,7			; GAP LENGTH VALUE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	MOV	DL,8			; FILLER BYTE TO NEC
	CALL	GET_PARM
	CALL	NEC_OUTPUT
	POP	AX			; THROW AWAY ERROR
	CALL	NEC_TERM		; TERMINATE, RECEIVE STATUS, ETC,
FM_DON:
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET
DISK_FORMAT	ENDP
;-------------------------------------------------------------------------------
; FNC_ERR:	INVALID FUNCTION REQUESTED OR INVALID DRIVE: SET BAD COMMAND IN
;		STATUS.
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
FNC_ERR PROC	NEAR			; INVALID FUNCTION REQUEST
	MOV	AX,SI			; RESTORE AL
	MOV	AH,BAD_CMD		; SET BAD COMMAND ERROR
	MOV	@DSKETTE_STATUS,AH	; STORE IN DATA AREA
	STC				; SET CARRY INDICATING ERROR
	RET
FNC_ERR 	ENDP
;-------------------------------------------------------------------------------
; DISK-PARMS:	READ DRIVE PARAMETERS.
;
; ON ENTRY:	DI : DRIVE #
;
; ON EXIT:	CL/[BP]   = BITS 7 & 6 HI 2 BITS OF MAX CYLINDER
;			    BITS 0-5 MAX SECTORS/TRACK
;		CH/[BP+1] = LOW 8 BITS OF MAX CYLINDER
;		BL/[BP+2] = BITS 7-4 = 0
;			    BITS 3-0 = VALID CMOS DRIVE TYPE
;		BH/[BP+3] = 0
;		DL/[BP+4] = # DRIVES INSTALLED (VALUE CHECKED)
;		DH/[BP+5] = MAX HEAD #
;		DI/[BP+6] = OFFSET TO DISK_BASE
;		ES	  = SEGMENT OF DISK_BASE
;		AX	  = 0
;
;		NOTE : THE ABOVE INFORMATION IS STORED IN THE USERS STACK AT
;		       THE LOCATIONS WHERE THE MAIN ROUTINE WILL POP THEM
;		       INTO THE APPROPRIATE REGISTERS BEFORE RETURNING TO THE
;		       CALLER.
;-------------------------------------------------------------------------------
DISK_PARMS	PROC	NEAR
	CMP	DI,80H			; CHECK FOR FIXED MEDIA TYPE REQUEST
	JB	DISK_P2 		; CONTINUE IF NOT REQUEST FALL THROUGH

;-----	FIXED DISK REQUEST FALL THROUGH ERROR

	MOV	AX,SI			; RESTORE AL WITH CALLERS VALUE
	MOV	AH,BAD_CMD		; SET BAD COMMAND ERROR IN (AH)
	STC				; SET ERROR RETURN CODE
	RET

DISK_P2:
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH,
	MOV	WORD PTR [BP+2],0	; DRIVE TYPE = 0
	MOV	AX,@EQUIP_FLAG		; LOAD EQUIPMENT FLAG FOR # DISKETTES
	AND	AL,11000001B		; KEEP DISKETTE DRIVE BITS
	MOV	DL,2			; DISKETTE DRIVES = 2
	CMP	AL,01000001B		; 2 DRIVES INSTALLED ?
	JZ	DISK_P3 		; IF YES JUMP

	DEC	DL			; DISKETTE DRIVES = 1
	CMP	AL,00000001B		; 1 DRIVE INSTALLED ?
	JNZ	DISK_P8 		; IF NO JUMP
DISK_P3:
	MOV	[BP+4],DL		; STORE NUMBER OF DRIVES
	CMP	DI,1			; CHECK FOR VALID DRIVE
	JA	DISK_P9 		; DRIVE INVALID
	MOV	BYTE PTR[BP+5],1	; MAXIMUM HEAD NUMBER = 1
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN AL
	JC	DISK_P4 		; IF CMOS BAD CHECKSUM ESTABLISHED
	OR	AL,AL			; TEST FOR NO DRIVE TYPE
	JZ	DISK_P4 		; JUMP IF SO
	CMP	AL,(DR_PTE-DR_PT)/2	; > MAXIMUM
	JA	DISK_P4 		; IF SO JUMP

	MOV	[BP+2],AL		; STORE VALID CMOS DRIVE TYPE
	DEC	AL			; MAKE 0 ORIGIN
	SHL	AL,1			; ACCOUNT FOR FIELD WIDTH
	MOV	BL,AL			; FINISH MAKING INDEX POINTER
	XOR	BH,BH			; CLEAR HIGH ORDER INDEX
	MOV	CX,CS:WORD PTR DR_PT[BX]; GET MAX TRACK AND SECTOR

DISK_P4:
	MOV	AL,@DSK_STATE[DI]	; LOAD STATE FOR THIS DRIVE
	TEST	AL,MED_DET		; CHECK FOR ESTABLISHED STATE
	JNZ	DISK_P5 		; GO TO CMOS FOR DRIVE CHECK
	CMP	BYTE PTR [BP+2],0	; CHECK FOR CMOS BAD/INVALID
	JZ	DISK_P9 		; CMOS BAD/INVALID AND UNESTABLISHED
	JMP	SHORT DISK_P6		; CMOS GOOD AND UNESTABLISHED; USE CMOS

DISK_P5:
	AND	AL,RATE_MSK		; ISOLATE STATE
	MOV	CX,WORD PTR CS:DR_PT+2	; GET DRIVE PARAMETERS FOR 1.2 M DRIVE
	CMP	AL,RATE_250		; 1.2M DRIVE ?
	JNE	DISK_P6 		; 300 OR 500 RATE IS 1.2M DRIVE

	MOV	CX,WORD PTR CS:DR_PT	; GET DRIVE PARAMETERS 360K DRIVE
	TEST	@DSK_STATE[DI],TRK_CAPA ; 80 TRACK ?
	JZ	DISK_P6 		; MUST BE 360

	MOV	CX,WORD PTR CS:DR_PT+4	; GET DRIVE PARAMETERS
DISK_P6:
	MOV	[BP],CX 		; SAVE POINTER IN STACK FOR RETURN
	LEA	AX,DISK_BASE		; ADDRESS OF DISK_BASE
	MOV	[BP+6],AX		; SAVE IN STACK
	MOV	AX,CS			; SEGMENT DISK_BASE (SAME AS THIS ONE)
DISK_P7:
	MOV	ES,AX			; ES IS SEGMENT OF TABLE
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	XOR	AX,AX			; CLEAR
	CLC
	RET

;-----	NO DRIVE PRESENT HANDLER

DISK_P8:
	MOV	BYTE PTR [BP+4],0	; CLEAR NUMBER OF DRIVES
DISK_P9:
	XOR	AX,AX			; CLEAR PARMS IF NO DRIVES OR CMOS BAD
	MOV	[BP],AX 		; TRACKS, SECTORS/TRACK = 0
	MOV	[BP+5],AH		; HEAD = 0
	MOV	[BP+6],AX		; OFFSET TO DISK_BASE = 0
	JMP	DISK_P7 		; EXIT
DISK_PARMS	ENDP
;-------------------------------------------------------------------------------
;	DRIVE PARAMETER TABLE
;-------------------------------------------------------------------------------
DR_PT	DB	09H,027H		; MAX. TRACKS, SECTORS/TRACK 360K
	DB	0FH,04FH		;      "          "          1.2M
	DB	09H,04FH		;      "          "          720K
DR_PTE	EQU	$
;-------------------------------------------------------------------------------
; DISK_TYPE:	THIS ROUTINE RETURNS THE TYPE OF MEDIA INSTALLED.
;
;  ON ENTRY:	DI     : DRIVE #
;
;  ON EXIT:	AH : DRIVE TYPE, CY=0
;-------------------------------------------------------------------------------
DISK_TYPE	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	MOV	AL,@DSK_STATE[DI]	; GET PRESENT STATE INFORMATION
	OR	AL,AL			; CHECK FOR NO DRIVE
	JZ	NO_DRV
	MOV	AH,NOCHGLN		; NO CHANGE LINE FOR 40 TRACK DRIVE
	TEST	AL,TRK_CAPA		; IS THIS DRIVE AN 80 TRACK DRIVE?
	JZ	DT_BACK 		; IF NO JUMP
	MOV	AH,CHGLN		; CHANGE LINE FOR 80 TRACK DRIVE

DT_BACK:
	PUSH	AX			; SAVE RETURN VALUE
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	POP	AX			; RESTORE RETURN VALUE
	CLC				; NO ERROR
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET
NO_DRV:
	XOR	AH,AH			; NO DRIVE PRESENT OR UNKNOWN
	JMP	SHORT DT_BACK
DISK_TYPE ENDP
;-------------------------------------------------------------------------------
; DISK_CHANGE : THIS ROUTINE RETURNS THE STATE OF THE DISK CHANGE LINE.
;
; ON ENTRY:	DI : DRIVE #
;
; ON EXIT:	AH : @DSKETTE_STATUS
;		00 - DISK CHANGE LINE INACTIVE, CY = 0
;		06 - DISK CHANGE LINE ACTIVE, CY = 1
;-------------------------------------------------------------------------------
DISK_CHANGE	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	MOV	AL,@DSK_STATE[DI]	; GET MEDIA STATE INFORMATION
	OR	AL,AL			; DRIVE PRESENT ?
	JZ	DC_NON			; JUMP IF NO DRIVE
	TEST	AL,TRK_CAPA		; 80 TRACK DRIVE ?
	JZ	SETIT			; IF SO , CHECK CHANGE LINE

	CALL	READ_DSKCHNG		; GO CHECK STATE OF DISK CHANGE LINE
	JZ	FINIS			; CHANGE LINE NOT ACTIVE

SETIT:	MOV	@DSKETTE_STATUS,MEDIA_CHANGE	; INDICATE MEDIA REMOVED

FINIS:	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	MOV	BX,SI			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET

DC_NON:
	OR	@DSKETTE_STATUS,TIME_OUT	; SET TIMEOUT, NO DRIVE
	JMP	SHORT FINIS
DISK_CHANGE ENDP
;-------------------------------------------------------------------------------
; FORMAT_SET :	THIS ROUTINE IS USED TO ESTABLISH THE TYPE OF MEDIA TO BE USED
;		FOR THE FOLLOWING FORMAT OPERATION.
;
; ON ENTRY:	SI LOW : DASD TYPE FOR FORMAT
;		DI     : DRIVE #
;
; ON EXIT:	@DSKETTE_STATUS REFLECTS STATUS
;		AH = @DSKETTE_STATUS
;		CY = 1 IF ERROR
;-------------------------------------------------------------------------------
FORMAT_SET	PROC	NEAR
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	PUSH	SI			; SAVE DASD TYPE
	MOV	AX,SI			; AH = ? , AL = DASD TYPE
	XOR	AH,AH			; AH = 0 , AL = DASD TYPE
	MOV	SI,AX			; SI = DASD TYPE
	AND	@DSK_STATE[DI],NOT MED_DET+DBL_STEP+RATE_MSK	; CLEAR STATE
	DEC	SI			; CHECK FOR 320/360K MEDIA & DRIVE
	JNZ	NOT_320 		; BYPASS IF NOT
	OR	@DSK_STATE[DI],MED_DET+RATE_250 ; SET TO 320/360
	JMP	SHORT S0

NOT_320:
	CALL	MED_CHANGE		; CHECK FOR TIME_OUT
	CMP	@DSKETTE_STATUS,TIME_OUT
	JZ	S0			; IF TIME OUT TELL CALLER

	DEC	SI			; CHECK FOR 320/360K IN 1.2M DRIVE
	JNZ	NOT_320_12		; BYPASS IF NOT
	OR	@DSK_STATE[DI],MED_DET+DBL_STEP+RATE_300 ; SET STATE
	JMP	SHORT S0

NOT_320_12:
	DEC	SI			; CHECK FOR 1.2M MEDIA IN 1.2M DRIVE
	JNZ	NOT_12			; BYPASS IF NOT
	OR	@DSK_STATE[DI],MED_DET+RATE_500 ; SET STATE VARIABLE
	JMP	SHORT S0		; RETURN TO CALLER

NOT_12:
	DEC	SI			; CHECK FOR SET DASD TYPE 04
	JNZ	FS_ERR			; BAD COMMAND EXIT IF NOT VALID TYPE

	TEST	@DSK_STATE[DI],DRV_DET	; DRIVE DETERMINED ?
	JZ	ASSUME			; IF STILL NOT DETERMINED ASSUME
	MOV	AL,MED_DET+RATE_300
	TEST	@DSK_STATE[DI],FMT_CAPA ; MULTIPLE FORMAT CAPABILITY ?
	JNZ	OR_IT_IN		; IF 1.2 M THEN DATA RATE 300

ASSUME:
	MOV	AL,MED_DET+RATE_250	; SET UP

OR_IT_IN:
	OR	@DSK_STATE[DI],AL	; OR IN THE CORRECT STATE

S0:
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	CALL	SETUP_END		; VARIOUS CLEANUPS
	POP	BX			; GET SAVED AL TO BL
	MOV	AL,BL			; PUT BACK FOR RETURN
	RET

FS_ERR:
	MOV	@DSKETTE_STATUS,BAD_CMD ; UNKNOWN STATE,BAD COMMAND
	JMP	SHORT S0

FORMAT_SET	ENDP
;-------------------------------------------------------------------------------
; XLAT_NEW:  TRANSLATES DISKETTE STATE LOCATIONS FROM COMPATIBLE MODE TO
;	     NEW ARCHITECTURE.
;
; ON ENTRY:	DI : DRIVE
;-------------------------------------------------------------------------------
XLAT_NEW	PROC	NEAR		;
	CMP	DI,1			; VALID DRIVE
	JA	XN_OUT			; IF INVALID BACK
	CMP	@DSK_STATE[DI],0	; NO DRIVE ?
	JZ	DO_DET			; IF NO DRIVE ATTEMPT DETERMINE
	MOV	CX,DI			; CX = DRIVE NUMBER
	SHL	CL,2			; CL = SHIFT COUNT, A=0, B=4
	MOV	AL,@HF_CNTRL		; DRIVE INFORMATION
	ROR	AL,CL			; TO LOW NIBBLE
	AND	AL,DRV_DET+FMT_CAPA+TRK_CAPA	; KEEP DRIVE BITS
	AND	@DSK_STATE[DI],NOT DRV_DET+FMT_CAPA+TRK_CAPA
	OR	@DSK_STATE[DI],AL	; UPDATE DRIVE STATE
XN_OUT:
	RET

DO_DET:
	CALL	DRIVE_DET		; TRY TO DETERMINE
	RET

XLAT_NEW	ENDP
;-------------------------------------------------------------------------------
; XLAT_OLD : TRANSLATES DISKETTE STATE LOCATIONS FROM NEW ARCHITECTURE TO
;	     COMPATIBLE MODE.
;
; ON ENTRY:	DI : DRIVE
;-------------------------------------------------------------------------------
XLAT_OLD	PROC	NEAR
	CMP	DI,1			; VALID DRIVE ?
	JA	XO_OUT			; IF INVALID BACK
	CMP	@DSK_STATE[DI],0	; NO DRIVE ?
	JZ	XO_OUT			; IF NO DRIVE TRANSLATE DONE

;-----	TEST FOR SAVED DRIVE INFORMATION ALREADY SET

	MOV	CX,DI			; CX = DRIVE NUMBER
	SHL	CL,2			; CL = SHIFT COUNT, A=0, B=4
	MOV	AH,FMT_CAPA		; LOAD MULTIPLE DATA RATE BIT MASK
	ROR	AH,CL			; ROTATE BY MASK
	TEST	@HF_CNTRL,AH		; MULTIPLE-DATA RATE DETERMINED ?
	JNZ	SAVE_SET		; IF SO, NO NEED TO RE-SAVE

;-----	ERASE DRIVE BITS IN @HF_CNTRL FOR THIS DRIVE

	MOV	AH,DRV_DET+FMT_CAPA+TRK_CAPA	; MASK TO KEEP
	ROR	AH,CL			; FIX MASK TO KEEP
	NOT	AH			; TRANSLATE MASK
	AND	@HF_CNTRL,AH		; KEEP BITS FROM OTHER DRIVE INTACT

;-----	ACCESS CURRENT DRIVE BITS AND STORE IN @HF_CNTRL

	MOV	AL,@DSK_STATE[DI]	; ACCESS STATE
	AND	AL,DRV_DET+FMT_CAPA+TRK_CAPA	; KEEP DRIVE BITS
	ROR	AL,CL			; FIX FOR THIS DRIVE
	OR	@HF_CNTRL,AL		; UPDATE SAVED DRIVE STATE

;-----	TRANSLATE TO COMPATIBILITY MODE

SAVE_SET:
	MOV	AH,@DSK_STATE[DI]	; ACCESS STATE
	MOV	BH,AH			; TO BH FOR LATER
	AND	AH,RATE_MSK		; KEEP ONLY RATE
	MOV	AL,M1D1U		; AL = 1.2 IN 1.2 UNESTABLISHED
	CMP	AH,RATE_500		; RATE 500 ?
	JZ	TST_DET 		; JUMP IF 1.2 IN 1.2
	MOV	AL,M3D1U		; AL = 360 IN 1.2 UNESTABLISHED
	CMP	AH,RATE_300		; RATE 300 ?
	JNZ	CHK_250 		; IF SO FALL THRU
	TEST	BH,DBL_STEP		; CHECK FOR DOUBLE STEP
	JNZ	TST_DET 		; MUST BE 360 IN 1.2

UNKNO:
	MOV	AL,MED_UNK		; NONE OF THE ABOVE
	JMP	SHORT AL_SET		; PROCESS COMPLETE

CHK_250:
	MOV	AL,M3D3U		; AL = 360 IN 360 UNESTABLISHED
	CMP	AH,RATE_250		; RATE 250 ?
	JNZ	UNKNO			; IF SO FALL THRU
	TEST	BH,TRK_CAPA		; 80 TRACK CAPABILITY ?
	JNZ	UNKNO			; IF SO JUMP, FALL THRU TEST DET

TST_DET:
	TEST	BH,MED_DET		; DETERMINED ?
	JZ	AL_SET			; IF NOT THEN SET
	ADD	AL,3			; MAKE DETERMINED/ESTABLISHED

AL_SET:
	AND	@DSK_STATE[DI],NOT DRV_DET+FMT_CAPA+TRK_CAPA  ; CLEAR DRIVE
	OR	@DSK_STATE[DI],AL	; REPLACE WITH COMPATIBLE MODE
XO_OUT:
	RET
XLAT_OLD	ENDP
;-------------------------------------------------------------------------------
; RD_WR_VF : COMMON READ, WRITE AND VERIFY: MAIN LOOP FOR STATE RETRIES.
;
; ON ENTRY:	AH : READ/WRITE/VERIFY DMA PARAMETER
;		AL : READ/WRITE/VERIFY NEC PARAMETER
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
RD_WR_VF	PROC	NEAR
	PUSH	AX			; SAVE DMA, NEC PARAMETERS
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH.
	CALL	SETUP_STATE		; STATE INITIALIZATIONS
	POP	AX			; RESTORE DMA,NEC PARAMETERS

DO_AGAIN:
	PUSH	AX			; SAVE READ/WRITE/VERIFY PARAMETER
	CALL	MED_CHANGE		; MEDIA CHANGE AND RESET IF CHANGED
	POP	AX			; RESTORE READ/WRITE/VERIFY
	JC	RWV_END 		; MEDIA CHANGE ERROR OR TIME-OUT
	PUSH	AX			; SAVE READ/WAIT/VERIFY PARAMETER
	CALL	SEND_RATE		; SEND DATA RATE TO NEC
	CALL	SETUP_DBL		; CHECK FOR DOUBLE STEP
	JC	CHK_RET 		; ERROR FROM READ ID, POSSIBLE RETRY
	POP	AX			; RESTORE NEC,DMA COMMAND
	PUSH	AX			; SAVE NEC COMMAND
	CALL	DMA_SETUP		; SET UP THE DMA
	POP	AX			; RESTORE NEC COMMAND
	JC	RWV_BAC 		; CHECK FOR DMA BOUNDARY ERROR
	PUSH	AX			; SAVE NEC COMMAND
	CALL	NEC_INIT		; INITIALIZE NEC
	CALL	RWV_COM 		; OP CODE COMMON TO READ/WRITE/VERIFY
	CALL	NEC_TERM		; TERMINATE, GET STATUS, ETC.

CHK_RET:
	CALL	RETRY			; CHECK FOR, SETUP RETRY
	POP	AX			; RESTORE READ/WRITE/VERIFY PARAMETER
	JC	DO_AGAIN		; CY = 1 MEANS RETRY

RWV_END:
	CALL	DSTATE			; ESTABLISH STATE IF SUCCESSFUL
	CALL	NUM_TRANS		; AL = NUMBER TRANSFERRED

RWV_BAC:				; BAD DMA ERROR ENTRY
	PUSH	AX			; SAVE NUMBER TRANSFERRED
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	POP	AX			; RESTORE NUMBER TRANSFERRED
	CALL	SETUP_END		; VARIOUS CLEANUPS
	RET
RD_WR_VF	ENDP
;-------------------------------------------------------------------------------
; SETUP_STATE:	INITIALIZES START AND END RATES.
;-------------------------------------------------------------------------------
SETUP_STATE	PROC	NEAR
	TEST	@DSK_STATE[DI],MED_DET	; MEDIA DETERMINED ?
	JNZ	J1C			; NO STATES IF DETERMINED
	MOV	AX,RATE_300*H+RATE_250	; AH = START RATE, AL = END RATE
	TEST	@DSK_STATE[DI],DRV_DET	; DRIVE ?
	JZ	AX_SET			; DO NOT KNOW DRIVE
	MOV	AL,RATE_500		; SET UP FOR 1.2 M END RATE
	TEST	@DSK_STATE[DI],FMT_CAPA ; 1.2 M ?
	JNZ	AX_SET			; JUMP WITH FIXED END RATE
	MOV	AX,RATE_250*X		; START A END RATE 250 FOR 360 DRIVE

AX_SET:
	AND	@DSK_STATE[DI],NOT RATE_MSK+DBL_STEP ; TURN OFF THE RATE
	OR	@DSK_STATE[DI],AH	; RATE FIRST TO TRY
	AND	@LASTRATE,NOT STRT_MSK	; ERASE LAST TO TRY RATE BITS
	ROR	AL,4			; TO OPERATION LAST RATE LOCATION
	OR	@LASTRATE,AL		; LAST RATE
J1C:
	RET
SETUP_STATE	ENDP
;-------------------------------------------------------------------------------
;  FMT_INIT: ESTABLISH STATE IF UNESTABLISHED AT FORMAT TIME.
;-------------------------------------------------------------------------------
FMT_INIT	PROC	NEAR
	TEST	@DSK_STATE[DI],MED_DET	; IS MEDIA ESTABLISHED
	JNZ	F1_OUT			; IF SO RETURN
	CALL	CMOS_TYPE		; RETURN DRIVE TYPE IN AL
	JC	CL_DRV			; ERROR IN CMOS ASSUME NO DRIVE
	DEC	AL			; MAKE ZERO ORIGIN
	JS	CL_DRV			; NO DRIVE IF AL 0
	MOV	AH,@DSK_STATE[DI]	; AH = CURRENT STATE
	AND	AH,NOT MED_DET+DBL_STEP+RATE_MSK	; CLEAR
	OR	AL,AL			; CHECK FOR 360
	JNZ	N_360			; IF 360 WILL BE 0
	OR	AH,MED_DET+RATE_250	; ESTABLISH MEDIA
	JMP	SHORT SKP_STATE 	; SKIP OTHER STATE PROCESSING

N_360:
	DEC	AL			; 1.2 M DRIVE
	JNZ	N_12			; JUMP IF NOT
	OR	AH,MED_DET+RATE_500	; DEFAULT TO 1.2M FORMAT
	JMP	SHORT SKP_STATE 	; SKIP OTHER STATE PROCESSING

N_12:
	DEC	AL			; CHECK FOR TYPE 3
	JNZ	CL_DRV			; NO DRIVE, CMOS BAD
	TEST	AH,DRV_DET		; IS DRIVE DETERMINED
	JZ	ISNT_12 		; TREAT AS NON 1.2 DRIVE
	TEST	AH,FMT_CAPA		; IS 1.2M
	JZ	ISNT_12 		; JUMP IF NOT
	OR	AH,MED_DET+RATE_300	; RATE 300
	JMP	SHORT SKP_STATE 	; CONTINUE

ISNT_12:
	OR	AH,MED_DET+RATE_250	; MUST BE RATE 250

SKP_STATE:
	MOV	@DSK_STATE[DI],AH	; STORE AWAY

F1_OUT:
	RET

CL_DRV:
	XOR	AH,AH			; CLEAR STATE
	JMP	SHORT SKP_STATE 	; SAVE IT
FMT_INIT	ENDP
;-------------------------------------------------------------------------------
; MED_CHANGE:	CHECKS FOR MEDIA CHANGE. RESETS MEDIA CHANGE, CHECKS MEDIA
;		CHANGE AGAIN.
;
; ON EXIT:	CY = 1 MEANS MEDIA CHANGE OR TIMEOUT
;		@DSKETTE_STATUS = ERROR CODE
;-------------------------------------------------------------------------------
MED_CHANGE	PROC	NEAR
	CALL	READ_DSKCHNG		; READ DISK CHANCE LINE STATE
	JZ	MED_C9			; BYPASS HANDLING DISK CHANGE LINE
	AND	@DSK_STATE[DI],NOT MED_DET	; CLEAR STATE FOR THIS DRIVE

;	THIS SEQUENCE ENSURES WHENEVER A DISKETTE IS CHANGED THAT
;	ON THE NEXT OPERATION THE REQUIRED MOTOR START UP TIME WILL
;	BE WAITED. (DRIVE MOTOR MAY GO OFF UPON DOOR OPENING).

	MOV	CX,DI			; CL = DRIVE 0
	MOV	AL,1			; MOTOR ON BIT MASK
	SHL	AL,CL			; TO APPROPRIATE POSITION
	NOT	AL			; KEEP ALL BUT MOTOR ON
	CLI				; NO INTERRUPTS
	AND	@MOTOR_STATUS,AL	; TURN MOTOR OFF INDICATOR
	STI				; INTERRUPTS ENABLED
	CALL	MOTOR_ON		; TURN MOTOR ON

;-----	THIS SEQUENCE OF SEEKS IS USED TO RESET DISKETTE CHANGE SIGNAL

	CALL	DISK_RESET		; RESET NEC
	MOV	CH,01H			; MOVE TO CYLINDER 1
	CALL	SEEK			; ISSUE SEEK
	XOR	CH,CH			; MOVE TO CYLINDER 0
	CALL	SEEK			; ISSUE SEEK
	MOV	@DSKETTE_STATUS,MEDIA_CHANGE	; STORE IN STATUS

	CALL	READ_DSKCHNG		; CHECK MEDIA CHANGED AGAIN
	JZ	MED_C8			; IF ACTIVE, NO DISKETTE, TIMEOUT

	MOV	@DSKETTE_STATUS,TIME_OUT; TIMEOUT IF DRIVE EMPTY
MED_C8:
	STC				; MEDIA CHANGED, SET CY
	RET
MED_C9:
	CLC				; NO MEDIA CHANGED, CLEAR CY
	RET
MED_CHANGE	ENDP
;-------------------------------------------------------------------------------
; SEND RATE:	SENDS DATA RATE COMMAND TO NEC IF PREVIOUS RATE WAS DIFFERENT '
;-------------------------------------------------------------------------------
SEND_RATE	PROC	NEAR
	MOV	AH,@LASTRATE		; GET LAST DATA RATE SELECTED
	MOV	AL,@DSK_STATE[DI]	; GET RATE STATE OF THIS DRIVE
	AND	AX,SEND_MSK*X		; KEEP ONLY RATE BITS OF BOTH
	CMP	AL,AH			; COMPARE TO PREVIOUSLY TRIED
	JE	C_S_OUT 		; IF SAME, NO NEW TRANSFER RATE

	AND	@LASTRATE,NOT SEND_MSK	; ELSE CLEAR LAST RATE ATTEMPTED
	OR	@LASTRATE,AL		; SAVE NEW RATE FOR NEXT CHECK
	ROL	AL,2			; MOVE TO BIT OUTPUT POSITIONS
	MOV	DX,03F7H		; OUTPUT NEW DATA RATE
	OUT	DX,AL

C_S_OUT:
	RET
SEND_RATE	ENDP
;-------------------------------------------------------------------------------
; DMA_SETUP:	THIS ROUTINE SETS UP THE DMA FOR READ/WRITE/VERIFY OPERATIONS.
;
; ON ENTRY:	AL = DMA COMMAND
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
DMA_SETUP	PROC	NEAR
	CLI				; DISABLE INTERRUPTS DURING DMA SET-UP
	OUT	DMA+12,AL		; SET THE FIRST/LAST F/F
	JMP	$+2			; WAIT FOR I/O
	OUT	DMA+11,AL		; OUTPUT THE MODE BYTE
	MOV	AX,ES			; GET THE ES VALUE
	ROL	AX,4			; ROTATE LEFT
	MOV	CH,AL			; GET HIGHEST NIBBLE OF ES TO CH
	AND	AL,11110000B		; ZERO THE LOW NIBBLE FROM SEGMENT
	ADD	AX,[BP+2]		; TEST FOR CARRY FROM ADDITION
	JNC	J33
	INC	CH			; CARRY MEANS HIGH 4 BITS MUST BE INC
J33:
	PUSH	AX			; SAVE START ADDRESS
	OUT	DMA+4,AL		; OUTPUT LOW ADDRESS
	JMP	$+2			; WAIT FOR I/O
	MOV	AL,AH
	OUT	DMA+4,AL		; OUTPUT HIGH ADDRESS
	MOV	AL,CH			; GET HIGH 4 BITS
	JMP	$+2			; I/O WAIT STATE
	AND	AL,00001111B
	OUT	081H,AL 		; OUTPUT HIGH 4 BITS TO PAGE REGISTER

;-----	DETERMINE COUNT

	MOV	AX,SI			; AL = # OF SECTORS
	XCHG	AL,AH			; AH = # OF SECTORS
	SUB	AL,AL			; AL = 0, AX = # OF SECTORS * 256
	SHR	AX,1			; AX = # SECTORS * 128
	PUSH	AX			; SAVE # OF SECTORS * 128
	MOV	DL,3			; GET BYTES/SECTOR PARAMETER
	CALL	GET_PARM		; "
	MOV	CL,AH			; SHIFT COUNT (0=128, 1=256 ETC)
	POP	AX			; AX = # OF SECTORS * 128
	SHL	AX,CL			; SHIFT BY PARAMETER VALUE
	DEC	AX			; -1 FOR DMA VALUE
	PUSH	AX			; SAVE COUNT VALUE
	OUT	DMA+5,AL		; LOW BYTE OF COUNT
	JMP	$+2			; WAIT FOR I/O
	MOV	AL,AH
	OUT	DMA+5,AL		; HIGH BYTE OF COUNT
	STI				; RE-ENABLE INTERRUPTS
	POP	CX			; RECOVER COUNT VALUE
	POP	AX			; RECOVER ADDRESS VALUE
	ADD	AX,CX			; ADD, TEST FOR 64K OVERFLOW
	MOV	AL,2			; MODE FOR 8237
	OUT	DMA+10,AL		; INITIALIZE THE DISKETTE CHANNEL
	JNC	NO_BAD			; CHECK FOR ERROR
	MOV	@DSKETTE_STATUS,DMA_BOUNDARY	; SET ERROR
NO_BAD:
	RET				; CY SET BY ABOVE IF ERROR
DMA_SETUP	ENDP
;-------------------------------------------------------------------------------
; NEC_INIT:	THIS ROUTINE SEEKS TO THE REQUESTED TRACK AND INITIALIZES THE
;		NEC FOR THE READ/WRITE/VERIFY/FORMAT OPERATION.
;
; ON ENTRY:	AH : NEC COMMAND TO BE PERFORMED
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
NEC_INIT	PROC	NEAR
	PUSH	AX			; SAVE NEC COMMAND
	CALL	MOTOR_ON		; TURN MOTOR ON FOR SPECIFIC DRIVE

;-----	DO THE SEEK OPERATION

	MOV	CH,[BP+1]		; CH = TRACK #
	CALL	SEEK			; MOVE TO CORRECT TRACK
	POP	AX			; RECOVER COMMAND
	JC	ER_1			; ERROR ON SEEK
	MOV	BX,OFFSET ER_1		; LOAD ERROR ADDRESS
	PUSH	BX			; PUSH NEC_OUT ERROR RETURN

;-----	SEND OUT THE PARAMETERS TO THE CONTROLLER

	CALL	NEC_OUTPUT		; OUTPUT THE OPERATION COMMAND
	MOV	AX,SI			; AH = HEAD #
	MOV	BX,DI			; BL = DRIVE #
	SAL	AH,2			; MOVE IT TO BIT 2
	AND	AH,00000100B		; ISOLATE THAT BIT
	OR	AH,BL			; OR IN THE DRIVE NUMBER
	CALL	NEC_OUTPUT		; FALL THRU CY SET IF ERROR
	POP	BX			; THROW AWAY ERROR RETURN
ER_1:
	RET
NEC_INIT		ENDP
;-------------------------------------------------------------------------------
; RWV_COM:	THIS ROUTINE SENDS PARAMETERS TO THE NEC SPECIFIC TO THE
;		READ/WRITE/VERIFY OPERATIONS.
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
RWV_COM PROC	NEAR
	MOV	AX,OFFSET ER_2		; LOAD ERROR ADDRESS
	PUSH	AX			; PUSH NEC_OUT ERROR RETURN
	MOV	AH,[BP+1]		; OUTPUT TRACK #
	CALL	NEC_OUTPUT
	MOV	AX,SI			; OUTPUT HEAD #
	CALL	NEC_OUTPUT
	MOV	AH,[BP] 		; OUTPUT SECTOR #
	CALL	NEC_OUTPUT
	MOV	DL,3			; BYTES/SECTOR PARAMETER FROM BLOCK
	CALL	GET_PARM		; . TO THE NEC
	CALL	NEC_OUTPUT		; OUTPUT TO CONTROLLER
	MOV	DL,4			; EOT PARAMETER FROM BLOCK
	CALL	GET_PARM		; . TO THE NEC
	CALL	NEC_OUTPUT		; OUTPUT TO CONTROLLER
	MOV	AL,@DSK_STATE[DI]	; GET DRIVE STATE VALUE
	MOV	AH,01BH 		; 1.2/1.2 DRIVE GAP LENGTH
	AND	AL,RATE_MSK		; STRIP OFF HIGH BITS
	JZ	R15			; IF SO JUMP
	MOV	AH,023H 		; 320,360/1.2 DRIVE GAP LENGTH
	DEC	AL			; CHECK FOR 320 MEDIA IN 1.2 DRIVE
	JZ	R15			; IF SO JUMP

	MOV	AH,02AH 		; 360/360 DRIVE GAP LENGTH
R15:
	CALL	NEC_OUTPUT
	MOV	DL,6			; DTL PARAMETER PROM BLOCK
	CALL	GET_PARM		;  TO THE NEC
	CALL	NEC_OUTPUT		; OUTPUT TO CONTROLLER
	POP	AX			; THROW AWAY ERROR EXIT
ER_2:
	RET
RWV_COM 	ENDP
;-------------------------------------------------------------------------------
; NEC_TERM:	THIS ROUTINE WAITS FOR THE OPERATION THEN ACCEPTS THE STATUS
;		FROM THE NEC FOR THE READ/WRITE/VERIFY/FORMAT OPERATION.
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
NEC_TERM		PROC	NEAR

;-----	LET THE OPERATION HAPPEN

	PUSH	SI			; SAVE HEAD #, # OF SECTORS
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	PUSHF
	CALL	RESULTS 		; GET THE NEC STATUS
	JC	SET_END_POP
	POPF
	JC	SET_END 		; LOOK FOR ERROR

;-----	CHECK THE RESULTS RETURNED BY THE CONTROLLER

	CLD				; SET THE CORRECT DIRECTION
	MOV	SI,OFFSET @NEC_STATUS	; POINT TO STATUS FIELD
	LODS	@NEC_STATUS		; GET ST0
	AND	AL,11000000B		; TEST FOR NORMAL TERMINATION
	JZ	SET_END 		;
	CMP	AL,01000000B		; TEST FOR ABNORMAL TERMINATION
	JNZ	J18			; NOT ABNORMAL, BAD NEC

;-----	ABNORMAL TERMINATION, FIND OUT WHY

	LODS	@NEC_STATUS		; GET STI
	SAL	AL,1			; TEST FOR EOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19
	SAL	AL,2
	MOV	AH,BAD_CRC
	JC	J19
	SAL	AL,1			; TEST FOR DMA OVERRUN
	MOV	AH,BAD_DMA
	JC	J19
	SAL	AL,2			; TEST FOR RECORD NOT FOUND
	MOV	AH,RECORD_NOT_FND
	JC	J19
	SAL	AL,1
	MOV	AH,WRITE_PROTECT	; TEST FOR WRITE_PROTECT
	JC	J19
	SAL	AL,1			; TEST MISSING ADDRESS MARK
	MOV	AH,BAD_ADDR_MARK
	JC	J19

;-----	NEC MUST HAVE FAILED
J18:
	MOV	AH,BAD_NEC
J19:
	OR	@DSKETTE_STATUS,AH
SET_END:
	CMP	@DSKETTE_STATUS,1	; SET ERROR CONDITION
	CMC				;
	POP	SI			; RESTORE HEAD #, # OF SECTORS
	RET

SET_END_POP:
	POPF
	JMP	SHORT SET_END
NEC_TERM	ENDP
;-------------------------------------------------------------------------------
; DSTATE:	ESTABLISH STATE UPON SUCCESSFUL OPERATION.
;-------------------------------------------------------------------------------
DSTATE	PROC	NEAR
	CMP	@DSKETTE_STATUS,0	; CHECK FOR ERROR
	JNZ	SETBAC			; IF ERROR JUMP
	OR	@DSK_STATE[DI],MED_DET	; NO ERROR, MARK MEDIA AS DETERMINED
	TEST	@DSK_STATE[DI],DRV_DET	; DRIVE DETERMINED ?
	JNZ	SETBAC			; IF DETERMINED NO TRY TO DETERMINE
	MOV	AL,@DSK_STATE[DI]	; LOAD STATE
	AND	AL,RATE_MSK		; KEEP ONLY RATE
	CMP	AL,RATE_250		; CHECK FOR 1.2M
	JNE	M_12			; MUST BE 1.2
	AND	@DSK_STATE[DI],NOT FMT_CAPA	; TURN OFF FORMAT CAPABILITY
	OR	@DSK_STATE[DI],DRV_DET	; MARK DRIVE DETERMINED
	JMP	SHORT SETBAC		; BACK

M_12:
	OR	@DSK_STATE[DI],DRV_DET+FMT_CAPA ; TURN ON DETERMINED & FMT CAPA

SETBAC:
	RET
DSTATE	ENDP
;-------------------------------------------------------------------------------
; RETRY:	DETERMINES WHETHER A RETRY IS NECESSARY. IF RETRY IS REQUIRED
;		THEN STATE INFORMATION IS UPDATED FOR RETRY.
;
; ON EXIT:	CY = 1 FOR RETRY, CY = 0 FOR NO RETRY
;-------------------------------------------------------------------------------
RETRY	PROC	NEAR
	CMP	@DSKETTE_STATUS,0	; GET STATUS OF OPERATION
	JZ	NO_RETRY		; SUCCESSFUL OPERATION
	CMP	@DSKETTE_STATUS,TIME_OUT	; IF TIME OUT NO RETRY
	JZ	NO_RETRY
	MOV	AH,@DSK_STATE[DI]	; GET MEDIA STATE OF DRIVE
	TEST	AH,MED_DET		; ESTABLISHED/DETERMINED ?
	JNZ	NO_RETRY		; IF ESTABLISHED STATE THEN TRUE ERROR
	AND	AH,RATE_MSK		; ISOLATE RATE
	MOV	CH,@LASTRATE		; GET START OPERATION STATE
	ROL	CH,4			; TO CORRESPONDING BITS
	AND	CH,RATE_MSK		; ISOLATE RATE BITS
	CMP	CH,AH			; ALL RATES TRIED
	JE	NO_RETRY		; IF YES, THEN TRUE ERROR

;	SETUP STATE INDICATOR FOR RETRY ATTEMPT TO NEXT RATE
;	  00000000B (500) -> 10000000B (250)
;	  10000000B (250) -> 01000000B (300)
;	  01000000B (300) -> 00000000B (500)

	CMP	AH,RATE_500+1		; SET CY FOR RATE 500
	RCR	AH,1			; TO NEXT STATE
	AND	AH,RATE_MSK		; KEEP ONLY RATE BITS
	AND	@DSK_STATE[DI],NOT RATE_MSK+DBL_STEP	; RATE, DBL STEP OFF
	OR	@DSK_STATE[DI],AH	; TURN ON NEW RATE
	MOV	@DSKETTE_STATUS,0	; RESET STATUS FOR RETRY
	STC				; SET CARRY FOR RETRY
	RET				; RETRY RETURN

NO_RETRY:
	CLC				; CLEAR CARRY NO RETRY
	RET				; NO RETRY RETURN
RETRY	ENDP
;-------------------------------------------------------------------------------
; NUM_TRANS:	THIS ROUTINE CALCULATES THE NUMBER OF SECTORS THAT WERE
;		ACTUALLY TRANSFERRED TO/FROM THE DISKETTE.
;
; ON ENTRY:	[BP+1] = TRACK
;		SI-HI  = HEAD
;		[BP]   = START SECTOR
;
; ON EXIT:	AL = NUMBER ACTUALLY TRANSFERRED
;-------------------------------------------------------------------------------
NUM_TRANS	PROC	NEAR
	XOR	AL,AL			; CLEAR FOR ERROR
	CMP	@DSKETTE_STATUS,0	; CHECK FOR ERROR
	JNZ	NT_OUT			; IF ERROR 0 TRANSFERRED
	MOV	DL,4			; SECTORS/TRACK OFFSET TO DL
	CALL	GET_PARM		; AH = SECTORS/TRACK
	MOV	BL,@NEC_STATUS+5	; GET ENDING SECTOR
	MOV	CX,SI			; CH = HEAD # STARTED
	CMP	CH,@NEC_STATUS+4	; GET HEAD ENDED UP ON
	JNZ	DIF_HD			; IF ON SAME HEAD, THEN NO ADJUST

	MOV	CH,@NEC_STATUS+3	; GET TRACK ENDED UP ON
	CMP	CH,[BP+1]		; IS IT ASKED FOR TRACK
	JZ	SAME_TRK		; IF SAME TRACK NO INCREASE

	ADD	BL,AH			; ADD SECTORS/TRACK
DIF_HD:
	ADD	BL,AH			; ADD SECTORS/TRACK
SAME_TRK:
	SUB	BL,[BP] 		; SUBTRACT START FROM END
	MOV	AL,BL			; TO AL

NT_OUT:
	RET
NUM_TRANS	ENDP
;-------------------------------------------------------------------------------
; SETUP_END:	RESTORES @MOTOR_COUNT TO PARAMETER PROVIDED IN TABLE AND LOADS
;		@DSKETTE_STATUS TO AH, AND SETS CY.
;
; ON EXIT:	AH, @DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
SETUP_END	PROC	NEAR
	MOV	DL,2			; GET THE MOTOR WAIT PARAMETER
	PUSH	AX			; SAVE NUMBER TRANSFERRED
	CALL	GET_PARM
	MOV	@MOTOR_COUNT,AH 	; STORE UPON RETURN
	POP	AX			; RESTORE NUMBER TRANSFERRED
	MOV	AH,@DSKETTE_STATUS	; GET STATUS OF OPERATION
	OR	AH,AH			; CHECK FOR ERROR
	JZ	NUN_ERR 		; NO ERROR
	XOR	AL,AL			; CLEAR NUMBER RETURNED

NUN_ERR:
	CMP	AH,1			; SET THE CARRY FLAG TO INDICATE
	CMC				; SUCCESS OR FAILURE
	RET
SETUP_END	ENDP
;-------------------------------------------------------------------------------
; SETUP_DBL:	CHECK DOUBLE STEP.
;
; ON ENTRY :	AH = RATE; DI = DRIVE
;
; ON EXIT :	CY = 1 MEANS ERROR
;-------------------------------------------------------------------------------
SETUP_DBL	PROC	NEAR
	MOV	AH,@DSK_STATE[DI]	; ACCESS STATE
	TEST	AH,MED_DET		; ESTABLISHED STATE ?
	JNZ	NO_DBL			; IF ESTABLISHED THEN DOUBLE DONE

;-----	CHECK FOR TRACK 0 TO SPEED UP ACKNOWLEDGE OF UNFORMATTED DISKETTE

	MOV	@SEEK_STATUS,0		; SET RECALIBRATE REQUIRED ON ALL DRIVES
	CALL	MOTOR_ON		; ENSURE MOTOR STAY ON
	MOV	CH,0			; LOAD TRACK 0
	CALL	SEEK			; SEEK TO TRACK 0
	CALL	READ_ID 		; READ ID FUNCTION
	JC	SD_ERR			; IF ERROR NO TRACK 0

;-----	INITIALIZE START AND MAX TRACKS (TIMES 2 FOR BOTH HEADS)

	MOV	CX,0450H		; START, MAX TRACKS
	TEST	@DSK_STATE[DI],TRK_CAPA ; TEST FOR 80 TRACK CAPABILITY
	JZ	CNT_OK			; IF NOT COUNT IS SETUP
	MOV	CL,0A0H 		; MAXIMUM TRACK 1.2 MB

;	ATTEMPT READ ID OF ALL TRACKS, ALL HEADS UNTIL SUCCESS; UPON SUCCESS,
;	MUST SEE IF ASKED FOR TRACK IN SINGLE STEP MODE = TRACK ID READ; IF NOT
;	THEN SET DOUBLE STEP ON.

CNT_OK:
	PUSH	CX			; SAVE TRACK, COUNT
	MOV	@DSKETTE_STATUS,0	; CLEAR STATUS, EXPECT ERRORS
	XOR	AX,AX			; CLEAR AX
	SHR	CH,1			; HALVE TRACK, CY = HEAD
	RCL	AL,3			; AX = HEAD IN CORRECT BIT
	PUSH	AX			; SAVE HEAD
	CALL	SEEK			; SEEK TO TRACK
	POP	AX			; RESTORE HEAD
	OR	DI,AX			; DI = HEAD OR'ED DRIVE
	CALL	READ_ID 		; READ ID HEAD 0
	PUSHF				; SAVE RETURN FROM READ_ID
	AND	DI,11111011B		; TURN OFF HEAD 1 BIT
	POPF				; RESTORE ERROR RETURN
	POP	CX			; RESTORE COUNT
	JNC	DO_CHK			; IF OK, ASKED = RETURNED TRACK ?
	INC	CH			; INC FOR NEXT TRACK
	CMP	CH,CL			; REACHED MAXIMUM YET
	JNZ	CNT_OK			; CONTINUE TILL ALL TRIED

;-----	FALL THRU, READ ID FAILED FOR ALL TRACKS

SD_ERR:
	STC				; SET CARRY FOR ERROR
	RET				; SETUP_DBL ERROR EXIT

DO_CHK:
	MOV	CL,@NEC_STATUS+3	; LOAD RETURNED TRACK
	MOV	@DSK_TRK[DI],CL 	; STORE TRACK NUMBER
	SHR	CH,1			; HALVE TRACK
	CMP	CH,CL			; IS IT THE SAME AS ASKED FOR TRACK
	JZ	NO_DBL			; IF SAME THEN NO DOUBLE STEP
	OR	@DSK_STATE[DI],DBL_STEP ; TURN ON DOUBLE STEP REQUIRED

NO_DBL:
	CLC				; CLEAR ERROR FLAG
	RET
SETUP_DBL	ENDP
;-------------------------------------------------------------------------------
; READ_ID	READ ID FUNCTION.
;
; ON ENTRY:	DI : BIT 2 = HEAD; BITS 1,0 = DRIVE
;
; ON EXIT:	DI : BIT 2 IS RESET, BITS 1,0 = DRIVE
;		@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION
;-------------------------------------------------------------------------------
READ_ID PROC	NEAR
	MOV	AX,OFFSET ER_3		; MOVE NEC OUTPUT ERROR ADDRESS

	PUSH	AX
	MOV	AH,4AH			; READ ID COMMAND
	CALL	NEC_OUTPUT		; TO CONTROLLER
	MOV	AX,DI			; DRIVE # TO AH, HEAD 0
	MOV	AH,AL
	CALL	NEC_OUTPUT		; TO CONTROLLER
	CALL	NEC_TERM		; WAIT FOR OPERATION, GET STATUS
	POP	AX			; THROW AWAY ERROR ADDRESS
ER_3:
	RET
READ_ID ENDP
;-------------------------------------------------------------------------------
; CMOS_TYPE:	RETURNS DISKETTE TYPE FROM CMOS
;
; ON ENTRY:	DI : DRIVE #
;
; ON EXIT:	AL = TYPE (IF VALID)	; CY REFLECTS STATUS
;-------------------------------------------------------------------------------
CMOS_TYPE	PROC	NEAR
	MOV	AL,CMOS_DIAG		; CMOS DIAGNOSTIC STATUS BYTE ADDRESS
	CALL	CMOS_READ		; GET CMOS STATUS
	TEST	AL,BAD_BAT+BAD_CKSUM	; BATTERY GOOD AND CHECKSUM VALID ?
	STC				; SET CY = 1 INDICATING ERROR FOR RETURN
	JNZ	CMOS_T9 		; ERROR EXIT IF EITHER ERROR BIT WAS ON
	MOV	AL,CMOS_DISKETTE	; ADDRESS OF DISKETTE BYTE IN CMOS
	CALL	CMOS_READ		; GET DISKETTE BYTE
	OR	DI,DI			; SEE WHICH DRIVE IN QUESTION
	JNZ	CMOS_T5 		; IF DRIVE 1, DATA IN LOW NIBBLE

	ROR	AL,4			; EXCHANGE NIBBLES IF SECOND DRIVE
CMOS_T5:
	AND	AL,00FH 		; KEEP ONLY DRIVE DATA, RESET CY = 0
CMOS_T9:
	RET				; CY = STATUS OF READ
CMOS_TYPE	ENDP
;-------------------------------------------------------------------------------
; GET_PARM:	THIS ROUTINE FETCHES THE INDEXED POINTER FROM THE DISK_BASE
;		BLOCK POINTED TO BY THE DATA VARIABLE @DISK_POINTER, A BYTE FROM
;		THAT TABLE IS THEN MOVED INTO AH, THE INDEX OF THAT BYTE BEING
;		THE PARAMETER IN DL.
;
; ON ENTRY:	DL = INDEX OF BYTE TO BE FETCHED
;
; ON EXIT:	AH = THAT BYTE FROM BLOCK
;		AL,DH DESTROYED
;-------------------------------------------------------------------------------
GET_PARM	PROC	NEAR
	PUSH	DS
	PUSH	SI
	SUB	AX,AX			; DS = 0 , BIOS DATA AREA
	MOV	DS,AX
	XCHG	DX,BX			; BL = INDEX
	SUB	BH,BH			; BX = INDEX
	ASSUME	DS:ABS0
	LDS	SI,@DISK_POINTER	; POINT TO BLOCK
	MOV	AH,[SI+BX]		; GET THE WORD
	XCHG	DX,BX			; RESTORE BX
	POP	SI
	POP	DS
	RET
	ASSUME	DS:DATA
GET_PARM	ENDP
;-------------------------------------------------------------------------------
; MOTOR_ON :	TURN MOTOR ON AND WAIT FOR MOTOR START UP TIME. THE @MOTOR_COUNT
;		IS REPLACED WITH A SUFFICIENTLY HIGH NUMBER (0FFH) TO ENSURE
;		THAT THE MOTOR DOES NOT GO OFF DURING THE OPERATION. IF THE
;		MOTOR NEEDED TO BE TURNED ON, THE MULTI-TASKING HOOK FUNCTION
;		(AX=90FDH, INT 15)  IS CALLED TELLING THE OPERATING SYSTEM
;		THAT THE BIOS IS ABOUT TO WAIT FOR MOTOR START UP. IF THIS
;		FUNCTION RETURNS WITH CY = 1, IT MEANS THAT THE MINIMUM WAIT
;		HAS BEEN COMPLETED. AT THIS POINT A CHECK IS MADE TO ENSURE
;		THAT THE MOTOR WASN'T TURNED OFF BY THE TIMER. IF THE HOOK DID
;		NOT WAIT, THE WAIT FUNCTION (AH=086H) IS CALLED TO WAIT THE
;		PRESCRIBED AMOUNT OF TIME. IF THE CARRY FLAG IS SET ON RETURN,
;		IT MEANS THAT THE FUNCTION IS IN USE AND DID NOT PERFORM THE
;		WAIT. A TIMER 1 WAIT LOOP WILL THEN DO THE WAIT.
;
; ON ENTRY:	DI = DRIVE #
;
; ON EXIT:	AX,BX,CX,DX DESTROYED
;-------------------------------------------------------------------------------
MOTOR_ON	PROC	NEAR
	CALL	TURN_ON 		; TURN ON MOTOR
	JC	MOT_IS_ON		; IF CY=1 NO WAIT
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	MOV	AX,090FDH		; LOAD WAIT CODE & TYPE
	INT	15H			; TELL OPERATING SYSTEM ABOUT TO DO WAIT
	PUSHF				; SAVE CY FOR TEST
	CALL	XLAT_NEW		; TRANSLATE STATE TO PRESENT ARCH,
	POPF				; RESTORE CY FOR TEST
	JNC	M_WAIT			; BYPASS LOOP IF OP SYSTEM HANDLED WAIT
	CALL	TURN_ON 		; CHECK AGAIN IF MOTOR ON
	JC	MOT_IS_ON		; IF NO WAIT MEANS IT IS ON

M_WAIT:
	MOV	DL,10			; GET THE MOTOR WAIT PARAMETER
	CALL	GET_PARM
	MOV	AL,AH			; AL = MOTOR WAIT PARAMETER
	XOR	AH,AH			; AX = MOTOR WAIT PARAMETER
	CMP	AL,8			; SEE IF AT LEAST A SECOND IS SPECIFIED
	JAE	GP2			; IF YES, CONTINUE
	MOV	AL,8			; ONE SECOND WAIT FOR MOTOR START UP

;-----	AS CONTAINS NUMBER OF 1/8 SECONDS (125000 MICROSECONDS) TO WAIT

GP2:	PUSH	AX			; SAVE WAIT PARAMETER
	MOV	DX,62500		; LOAD LARGEST POSSIBLE MULTIPLIER
	MUL	DX			; MULTIPLY BY HALF OF WHAT'S NECESSARY
	MOV	CX,DX			; CX = HIGH WORD
	MOV	DX,AX			; CX,DX = 1/2 * (# OF MICROSECONDS)
	CLC				; CLEAR CARRY FOR ROTATE
	RCL	DX,1			; DOUBLE LOW WORD, CY CONTAINS OVERFLOW
	RCL	CX,1			; DOUBLE HI, INCLUDING LOW WORD OVERFLOW
	MOV	AH,86H			; LOAD WAIT CODE
	INT	15H			; PERFORM WAIT
	POP	AX			; RESTORE WAIT PARAMETER
	JNC	MOT_IS_ON		; CY MEANS WAIT COULD NOT BE DONE

;-----	FOLLOWING LOOPS REQUIRED WHEN RTC WAIT FUNCTION IS ALREADY IN USE

J13:					;	WAIT FOR 1/8 SECOND PER (AL)
	MOV	CX,8286 		; COUNT FOR 1/8 SECOND AT 15.085737 US
	CALL	WAITF			; GO TO FIXED WAIT ROUTINE
	DEC	AL			; DECREMENT TIME VALUE
	JNZ	J13			; ARE WE DONE YET

MOT_IS_ON:
	RET
MOTOR_ON		ENDP
;-------------------------------------------------------------------------------
; TURN_ON :	TURN MOTOR ON AND RETURN WAIT STATE.
;
; ON ENTRY:	 DI = DRIVE #
;
; ON EXIT:	 CY = 0 MEANS WAIT REQUIRED
;		 CY = 1 MEANS NO WAIT REQUIRED
;		 AX,BX,CX,DX DESTROYED
;-------------------------------------------------------------------------------
TURN_ON PROC	NEAR
	MOV	BX,DI			; BX = DRIVE #
	MOV	CL,BL			; CL = DRIVE #
	ROL	BL,4			; BL = DRIVE SELECT
	CLI				; NO INTERRUPTS WHILE DETERMINING STATUS
	MOV	@MOTOR_COUNT,0FFH	; ENSURE MOTOR STAYS ON FOR OPERATION
	MOV	AL,@MOTOR_STATUS	; GET DIGITAL OUTPUT REGISTER REFLECTION
	AND	AL,00110000B		; KEEP ONLY DRIVE SELECT BITS
	MOV	AH,1			; MASK FOR DETERMINING MOTOR BIT
	SHL	AH,CL			; AH = MOTOR ON, A=00000001, B=00000010

;  AL = DRIVE SELECT FROM @MOTOR_STATUS
;  BL = DRIVE SELECT DESIRED
;  AH = MOTOR ON MASK DESIRED

	CMP	AL,BL			; REQUESTED DRIVE ALREADY SELECTED ?
	JNZ	TURN_IT_ON		; IF NOT SELECTED JUMP
	TEST	AH,@MOTOR_STATUS	; TEST MOTOR ON BIT
	JNZ	NO_MOT_WAIT		; JUMP IF MOTOR ON AND SELECTED

TURN_IT_ON:
	OR	AH,BL			; AH = DRIVE SELECT AND MOTOR ON
	MOV	BH,@MOTOR_STATUS	; SAVE COPY OF @MOTOR_STATUS BEFORE
	AND	BH,00001111B		; KEEP ONLY MOTOR BITS
	AND	@MOTOR_STATUS,11001111B ; CLEAR OUT DRIVE SELECT
	OR	@MOTOR_STATUS,AH	; OR IN DRIVE SELECTED AND MOTOR ON
	MOV	AL,@MOTOR_STATUS	; GET DIGITAL OUTPUT REGISTER REFLECTION
	MOV	BL,AL			; BL=@MOTOR_STATUS AFTER, BH=BEFORE
	AND	BL,00001111B		; KEEP ONLY MOTOR BITS
	STI				; ENABLE INTERRUPTS AGAIN
	AND	AL,00111111B		; STRIP AWAY UNWANTED BITS
	ROL	AL,4			; PUT BITS IN DESIRED POSITIONS
	OR	AL,00001100B		; NO RESET, ENABLE DMA/INTERRUPT
	MOV	DX,03F2H		; SELECT DRIVE AND TURN ON MOTOR
	OUT	DX,AL			; "
	CMP	BL,BH			; NEW MOTOR TURNED ON
	JZ	NO_MOT_WAIT		; NO WAIT REQUIRED IF JUST SELECT
	CLC				; SET CARRY MEANING WAIT
	RET

NO_MOT_WAIT:
	STC				; SET NO WAIT REQUIRED
	STI				; INTERRUPTS BACK ON
	RET
TURN_ON ENDP
;-------------------------------------------------------------------------------
; HD_WAIT :	WAIT FOR HEAD SETTLE TIME.
;
; ON ENTRY:	DI : DRIVE #
;
; ON EXIT:	AX,BX,CX,DX DESTROYED
;-------------------------------------------------------------------------------
HD_WAIT 	PROC	NEAR
	MOV	DL,9			; GET HEAD SETTLE PARAMETER
	CALL	GET_PARM		; "
	TEST	@MOTOR_STATUS,10000000B ; SEE IF A WRITE OPERATION
	JZ	ISNT_WRITE		; IF NOT, DO NOT ENFORCE ANY VALUES
	OR	AH,AH			; CHECK FOR ANY WAIT?
	JNZ	DO_WAT			; IF THERE DO NOT ENFORCE
	MOV	AH,HD12_SETTLE		; LOAD 1.2M HEAD SETTLE MINIMUM
	MOV	AL,@DSK_STATE[DI]	; LOAD STATE
	AND	AL,RATE_MSK		; KEEP ONLY RATE
	CMP	AL,RATE_250		; 1.2 M DRIVE ?
	JNZ	DO_WAT			; DEFAULT HEAD SETTLE LOADED

	MOV	AH,HD320_SETTLE 	; USE 320/360 HEAD SETTLE
	JMP	SHORT DO_WAT		; "

ISNT_WRITE:
	OR	AH,AH			; CHECK FOR NO WAIT
	JZ	HW_DONE 		; IF NOT WRITE AND 0 ITS OK

;-----	AH CONTAINS NUMBER OF MILLISECONDS TO WAIT

DO_WAT:
	MOV	AL,AH			; AL = # MILLISECONDS
	XOR	AH,AH			; AX = # MILLISECONDS
	PUSH	AX			; SAVE HEAD SETTLE PARAMETER
	MOV	DX,1000 		; SET UP FOR MULTIPLY TO MICROSECONDS
	MUL	DX			; DX,AX = # MICROSECONDS
	MOV	CX,DX			; CX,AX = # MICROSECONDS
	MOV	DX,AX			; CX,DX = # MICROSECONDS
	MOV	AH,86H			; LOAD WAIT CODE
	INT	15H			; PERFORM WAIT
	POP	AX			; RESTORE HEAD SETTLE PARAMETER
	JNC	HW_DONE 		; CHECK FOR EVENT WAIT ACTIVE

J29:					;	1 MILLISECOND LOOP
	MOV	CX,66			; COUNT AT 15.085737 US PER COUNT
	CALL	WAITF			; DELAY FOR 1 MILLISECOND
	DEC	AL			; DECREMENT THE COUNT
	JNZ	J29			; DO AL MILLISECOND # OF TIMES
HW_DONE:
	RET
HD_WAIT 	ENDP
;-------------------------------------------------------------------------------
; NEC_OUTPUT:	THIS ROUTINE SENDS A BYTE TO THE NEC CONTROLLER AFTER TESTING
;		FOR CORRECT DIRECTION AND CONTROLLER READY THIS ROUTINE WILL
;		TIME OUT IF THE BYTE IS NOT ACCEPTED WITHIN A REASONABLE AMOUNT
;		OF TIME, SETTING THE DISKETTE STATUS ON COMPLETION.
;
; ON ENTRY:	AH = BYTE TO BE OUTPUT
;
; ON EXIT:	CY = 0	SUCCESS
;		CY = 1	FAILURE -- DISKETTE STATUS UPDATED
;			IF A FAILURE HAS OCCURRED, THE RETURN IS MADE ONE LEVEL
;			HIGHER THAN THE CALLER OF NEC OUTPUT. THIS REMOVES THE
;			REQUIREMENT OF TESTING AFTER EVERY CALL OF NEC_OUTPUT.
;		AX,BX,CX,DX DESTROYED
;-------------------------------------------------------------------------------
NEC_OUTPUT	PROC	NEAR
	MOV	DX,03F4H		; STATUS PORT
	MOV	BL,2			; HIGH ORDER COUNTER
	XOR	CX,CX			; COUNT FOR TIME OUT

J23:	IN	AL,DX			; GET STATUS
	AND	AL,11000000B		; KEEP STATUS AND DIRECTION
	CMP	AL,10000000B		; STATUS 1 AND DIRECTION 0 ?
	JZ	J27			; STATUS AND DIRECTION OK
	LOOP	J23			; CONTINUE TILL CX EXHAUSTED

	DEC	BL			; DECREMENT COUNTER
	JNZ	J23			; REPEAT TILL DELAY FINISHED, CX = 0

;-----	FALL THRU TO ERROR RETURN

	OR	@DSKETTE_STATUS,TIME_OUT
	POP	AX			; DISCARD THE RETURN ADDRESS
	STC				; INDICATE ERROR TO CALLER
	RET

;-----	DIRECTION AND STATUS OK: OUTPUT BYTE

J27:	MOV	AL,AH			; GET BYTE TO OUTPUT
	INC	DX			; DATA PORT = STATUS PORT + 1
	OUT	DX,AL			; OUTPUT THE BYTE
	RET				; CY = 0 FROM TEST INSTRUCTION
NEC_OUTPUT	ENDP
;-------------------------------------------------------------------------------
; SEEK: 	THIS ROUTINE WILL MOVE THE HEAD ON THE NAMED DRIVE TO THE NAMED
;		TRACK.	IF THE DRIVE HAS NOT BEEN ACCESSED SINCE THE DRIVE
;		RESET COMMAND WAS ISSUED, THE DRIVE WILL BE RECALIBRATED.
;
; ON ENTRY:	DI : DRIVE #
;		CH : TRACK #
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.
;		AX,BX,CX DX DESTROYED
;-------------------------------------------------------------------------------
SEEK	PROC	NEAR
	MOV	BX,DI			; BX = DRIVE #
	MOV	DX,OFFSET NEC_ERR	; LOAD RETURN ADDRESS
	PUSH	DX			; ON STACK FOR NEC_OUTPUT ERROR
	MOV	AL,1			; ESTABLISH MASK FOR RECALIBRATE TEST
	XCHG	CL,BL			; SET DRIVE VALULE INTO CL
	ROL	AL,CL			; SHIFT MASK BY THE DRIVE VALUE
	XCHG	CL,BL			; RECOVER TRACK VALUE
	TEST	AL,@SEEK_STATUS 	; TEST FOR RECALIBRATE REQUIRED
	JNZ	J28A			; JUMP IF RECALIBRATE NOT REQUIRED

	OR	@SEEK_STATUS,AL 	; TURN ON THE NO RECALIBRATE BIT IN FLAG
	CALL	RECAL			; RECALIBRATE DRIVE
	JNC	AFT_RECAL		; RECALIBRATE DONE

;-----	ISSUE RECALIBRATE FOR 80 TRACK DISKETTES

	MOV	@DSKETTE_STATUS,0	; CLEAR OUT INVALID STATUS
	CALL	RECAL			; RECALIBRATE DRIVE
	JC	RB			; IF RECALIBRATE FAILS TWICE THEN ERROR
;
AFT_RECAL:
	MOV	@DSK_TRK[DI],0		; SAVE NEW CYLINDER AS PRESENT POSITION
	OR	CH,CH			; CHECK FOR SEEK TO TRACK 0
	JZ	DO_WAIT 		; HEAD SETTLE, CY = 0 IF JUMP

;-----	DRIVE IS IN SYNCHRONIZATION WITH CONTROLLER, SEEK TO TRACK

J28A:	TEST	@DSK_STATE[DI],DBL_STEP ; CHECK FOR DOUBLE STEP REQUIRED
	JZ	R7			; SINGLE STEP REQUIRED BYPASS DOUBLE
	SHL	CH,1			; DOUBLE NUMBER OF STEP TO TAKE

R7:	CMP	CH,@DSK_TRK[DI] 	; SEE IF ALREADY AT THE DESIRED TRACK
	JE	RB			; IF YES, DO NOT NEED TO SEEK

	MOV	@DSK_TRK[DI],CH 	; SAVE NEW CYLINDER AS PRESENT POSITION
	MOV	AH,0FH			; SEEK COMMAND TO NEC
	CALL	NEC_OUTPUT
	MOV	BX,DI			; BX = DRIVE #
	MOV	AH,BL			; OUTPUT DRIVE NUMBER
	CALL	NEC_OUTPUT
	MOV	AH,@DSK_TRK[DI] 	; GET CYLINDER NUMBER
	CALL	NEC_OUTPUT
	CALL	CHK_STAT_2		; ENDING INTERRUPT AND SENSE STATUS

;-----	WAIT FOR HEAD SETTLE

DO_WAIT:
	PUSHF				; SAVE STATUS
	CALL	HD_WAIT 		; WAIT FOR HEAD SETTLE TIME
	POPF				; RESTORE STATUS
RB:
	POP	AX			; CLEAR ERROR RETURN FROM NEC_OUTPUT
NEC_ERR:
	RET				; RETURN TO CALLER
SEEK	ENDP
;-------------------------------------------------------------------------------
; RECAL :	RECALIBRATE DRIVE
;
; ON ENTRY	DI = DRIVE #
;
; ON EXIT:	CY  REFLECTS STATUS OF OPERATION.
;-------------------------------------------------------------------------------
RECAL	PROC	NEAR
	PUSH	CX
	MOV	AX,OFFSET RC_BACK	; LOAD NEC_OUTPUT ERROR
	PUSH	AX
	MOV	AH,07H			; RECALIBRATE COMMAND
	CALL	NEC_OUTPUT
	MOV	BX,DI			; DX = DRIVE #
	MOV	AH,BL
	CALL	NEC_OUTPUT		; OUTPUT THE DRIVE NUMBER
	CALL	CHK_STAT_2		; GET THE INTERRUPT AND SENSE INT STATUS
	POP	AX			; THROW AWAY ERROR
RC_BACK:
	POP	CX
	RET
RECAL	ENDP
;-------------------------------------------------------------------------------
; CHK_STAT_2:	THIS ROUTINE HANDLES THE INTERRUPT RECEIVED AFTER RECALIBRATE,
;		SEEK, OR RESET TO THE ADAPTER. THE INTERRUPT IS WAITED FOR, THE
;		INTERRUPT STATUS SENSED, AND THE RESULT RETURNED TO THE CALLER.
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.
;-------------------------------------------------------------------------------
CHK_STAT_2	PROC	NEAR
	MOV	AX,OFFSET CS_BACK	; LOAD NEC_OUTPUT ERROR ADDRESS
	PUSH	AX
	CALL	WAIT_INT		; WAIT FOR THE INTERRUPT
	JC	J34			; IF ERROR, RETURN IT
	MOV	AH,08H			; SENSE INTERRUPT STATUS COMMAND
	CALL	NEC_OUTPUT
	CALL	RESULTS 		; READ IN THE RESULTS
	JC	J34
	MOV	AL,@NEC_STATUS		; GET THE FIRST STATUS BYTE
	AND	AL,01100000B		; ISOLATE THE BITS
	CMP	AL,01100000B		; TEST FOR CORRECT VALUE
	JZ	J35			; IF ERROR, GO MARK IT
	CLC				; GOOD RETURN
J34:
	POP	AX			; THROW AWAY ERROR RETURN
CS_BACK:
	RET

J35:
	OR	@DSKETTE_STATUS,BAD_SEEK
	STC				; ERROR RETURN CODE
	JMP	SHORT J34
CHK_STAT_2	ENDP
;-------------------------------------------------------------------------------
; WAIT_INT:	THIS ROUTINE WAITS FOR AN INTERRUPT TO OCCUR A TIME OUT ROUTINE
;		TAKES PLACE DURING THE WAIT, SO THAT AN ERROR MOV BE RETURNED
;		IF THE DRIVE IS NOT READY.
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.
;-------------------------------------------------------------------------------
WAIT_INT	PROC	NEAR
	STI				; TURN ON INTERRUPTS, JUST IN CASE
	CLC				; CLEAR TIMEOUT INDICATOR
	MOV	AX,09001H		; LOAD WAIT CODE AND TYPE
	INT	15H			; PERFORM OTHER PUNCTION
	JC	J36A			; BYPASS TIMING LOOP IF TIMEOUT DONE

	MOV	BL,4			; CLEAR THE COUNTERS
	XOR	CX,CX			; FOR 2 SECOND WAIT
J36:
	TEST	@SEEK_STATUS,INT_FLAG	; TEST FOR INTERRUPT OCCURRING
	JNZ	J37
	LOOP	J36			; COUNT DOWN WHILE WAITING
	DEC	BL			; SECOND LEVEL COUNTER
	JNZ	J36

J36A:	OR	@DSKETTE_STATUS,TIME_OUT	; NOTHING HAPPENED
	STC				; ERROR RETURN
J37:
	PUSHF				; SAVE CURRENT CARRY
	AND	@SEEK_STATUS,NOT INT_FLAG	; TURN OFF INTERRUPT FLAG
	POPF				; RECOVER CARRY
	RET				; GOOD RETURN CODE
WAIT_INT	ENDP
;-------------------------------------------------------------------------------
; RESULTS:	THIS ROUTINE WILL READ ANYTHING THAT THE NEC CONTROLLER RETURNS
;		FOLLOWING AN INTERRUPT.
;
; ON EXIT:	@DSKETTE_STATUS, CY REFLECT STATUS OF OPERATION.
;		AX,BX,CX,DX DESTROYED
;-------------------------------------------------------------------------------
RESULTS PROC	NEAR
	PUSH	DI
	MOV	DI,OFFSET @NEC_STATUS	; POINTER TO DATA AREA
	MOV	BL,7			; MAX STATUS BYTES
	MOV	DX,03F4H		; STATUS PORT

;-----	WAIT FOR REQUEST FOR MASTER

R10:	MOV	BH,2			; HIGH ORDER COUNTER
	XOR	CX,CX			; COUNTER
J39:					; WAIT FOR MASTER
	IN	AL,DX			; GET STATUS
	AND	AL,11000000B		; KEEP ONLY STATUS AND DIRECTION
	CMP	AL,11000000B		; STATUS 1 AND DIRECTION 0 ?
	JZ	J42			; STATUS AND DIRECTION OK
	LOOP	J39			; LOOP TILL TIMEOUT

	DEC	BH			; DECREMENT HIGH ORDER COUNTER
	JNZ	J39			; REPEAT TILL DELAY DONE

	OR	@DSKETTE_STATUS,TIME_OUT
	STC				; SET ERROR RETURN
	JMP	SHORT POPRES		; POP REGISTERS AND RETURN

;-----	READ IN THE STATUS

J42:
	INC	DX			; POINT AT DATA PORT
	IN	AL,DX			; GET THE DATA
	MOV	[DI],AL 		; STORE THE BYTE
	INC	DI			; INCREMENT THE POINTER

	MOV	CX,2			; MINIMUM 12 MICROSECONDS FOR NEC
	CALL	WAITF			; WAIT 15 TO 30 MICROSECONDS
	DEC	DX			; POINT AT STATUS PORT
	IN	AL,DX			; GET STATUS
	TEST	AL,00010000B		; TEST FOR NEC STILL BUSY
	JZ	POPRES			; RESULTS DONE ?

	DEC	BL			; DECREMENT THE STATUS COUNTER
	JNZ	R10			; GO BACK FOR MORE
	OR	@DSKETTE_STATUS,BAD_NEC ; TOO MANY STATUS BYTES
	STC				; SET ERROR FLAG

;-----	RESULT OPERATION IS DONE

POPRES:
	POP	DI
	RET				; RETURN WITH CARRY SET
RESULTS 	ENDP
;-------------------------------------------------------------------------------
; READ_DSKCHNG: READS THE STATE OF THE DISK CHANGE LINE.
;
; ON ENTRY:	DI = DRIVE #
;
; ON EXIT:	DI = DRIVE #
;		ZERO FLAG = 0 : DISK CHANGE LINE INACTIVE
;		ZERO FLAG - 1 : DISK CHANGE LINE ACTIVE
;		AX,CX,DX DESTROYED
;-------------------------------------------------------------------------------
READ_DSKCHNG	PROC	NEAR
	CALL	MOTOR_ON		; TURN ON THE MOTOR IF OFF
	MOV	DX,03F7H		; ADDRESS DIGITAL INPUT REGISTER
	IN	AL,DX			; INPUT DIGITAL INPUT REGISTER
	TEST	AL,DSK_CHG		; CHECK FOR DISK CHANGE LINE ACTIVE
	RET				; RETURN TO CALLER WITH ZERO FLAG SET
READ_DSKCHNG	ENDP
;-------------------------------------------------------------------------------
; DRIVE_DET:	DETERMINES WHETHER DRIVE IS 80 OR 40 TRACKS AND UPDATES STATE
;		INFORMATION ACCORDINGLY.
;
; ON ENTRY:	DI = DRIVE #
;-------------------------------------------------------------------------------
DRIVE_DET	PROC	NEAR
	CALL	MOTOR_ON		; TURN ON MOTOR IF NOT ALREADY ON
	CALL	RECAL			; RECALIBRATE DRIVE
	JC	DD_BAC			; ASSUME NO DRIVE PRESENT
	MOV	CH,TRK_SLAP		; SEEK TO TRACK 48
	CALL	SEEK			; "
	JC	DD_BAC			; ERROR NO DRIVE
	MOV	CH,QUIET_SEEK+1 	; SEEK TO TRACK 10
SK_GIN:
	DEC	CH			; DECREMENT TO NEXT TRACK
	PUSH	CX			; SAVE TRACK
	CALL	SEEK			; "
	JC	POP_BAC 		; POP AND RETURN
	MOV	AX,OFFSET DD_BAC	; LOAD NEC OUTPUT ERROR ADDRESS
	PUSH	AX			; "
	MOV	AH,SENSE_DRV_ST 	; SENSE DRIVE STATUS COMMAND BYTE
	CALL	NEC_OUTPUT		; OUTPUT TO NEC
	MOV	AX,DI			; AL = DRIVE
	MOV	AH,AL			; AH = DRIVE
	CALL	NEC_OUTPUT		; OUTPUT TO NEC
	CALL	RESULTS 		; GO GET STATUS
	POP	AX			; THROW AWAY ERROR ADDRESS
	POP	CX			; RESTORE TRACK
	TEST	@NEC_STATUS,HOME	; TRACK 0 ?
	JZ	SK_GIN			; GO TILL TRACK 0
	OR	CH,CH			; IS HOME AT TRACK 0
	JZ	IS_80			; MUST BE 80 TRACK DRIVE

;	DRIVE IS A 360; SET DRIVE TO DETERMINED;
;	SET MEDIA TO DETERMINED AT RATE 250.

	OR	@DSK_STATE[DI],DRV_DET+MED_DET+RATE_250
	RET				; ALL INFORMATION SET

IS_80:
	OR	@DSK_STATE[DI],TRK_CAPA ; SETUP 80 TRACK CAPABILITY
DD_BAC:
	RET

POP_BAC:
	POP	CX			; THROW AWAY
	RET
DRIVE_DET	ENDP
PAGE
;--- HARDWARE INT  0E H -- ( IRQ LEVEL	6 ) ------------------------------------
;
; DISK_INT	THIS ROUTINE HANDLES THE DISKETTE INTERRUPT.
;
; ON EXIT:	THE INTERRUPT FLAG IS SET IN @SEEK_STATUS.
;
;-------------------------------------------------------------------------------

DISK_INT_1	PROC	FAR		; ENTRY POINT FOR ORG 0EF57H
	STI				; RE-ENABLE INTERRUPTS
	PUSH	AX			; SAVE WORK REGISTER
	PUSH	DS			; SAVE REGISTERS
	CALL	DDS			; SETUP DATA ADDRESSING
	OR	@SEEK_STATUS,INT_FLAG	; TURN ON INTERRUPT OCCURRED
	POP	DS			; RESTORE USER (DS)
	MOV	AL,EOI			; END OF INTERRUPT MARKER
	OUT	INTA00,AL		; INTERRUPT CONTROL PORT
	MOV	AX,09101H		; INTERRUPT POST CODE AND TYPE
	INT	15H			; GO PERFORM OTHER TASK
	POP	AX			; RECOVER REGISTER
	IRET				; RETURN FROM INTERRUPT
DISK_INT_1	ENDP


;-------------------------------------------------------------------------------
; DSKETTE_SETUP: THIS ROUTINE DOES A PRELIMINARY CHECK TO SEE WHAT TYPE OF
;		 DISKETTE DRIVES ARE ATTACH TO THE SYSTEM.
;-------------------------------------------------------------------------------

DSKETTE_SETUP PROC	NEAR
	PUSH	AX			; SAVE REGISTERS
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	DI
	PUSH	DS
	CALL	DDS			; POINT DATA SEGMENT TO BIOS DATA AREA
	OR	@RTC_WAIT_FLAG,01	; NO RTC WAIT, FORCE USE OF LOOP
	XOR	DI,DI			; INITIALIZE DRIVE POINTER
	MOV	WORD PTR @DSK_STATE,0	; INITIALIZE STATES
	AND	@LASTRATE,NOT STRT_MSK+SEND_MSK ; CLEAR START & SEND
	OR	@LASTRATE,SEND_MSK	; INITIALIZE SENT TO IMPOSSIBLE
	MOV	@SEEK_STATUS,0		; INDICATE RECALIBRATE NEEDED
	MOV	@MOTOR_COUNT,0		; INITIALIZE MOTOR COUNT
	MOV	@MOTOR_STATUS,0 	; INITIALIZE DRIVES TO OFF STATE
	MOV	@DSKETTE_STATUS,0	; NO ERRORS

SUP0:
	CALL	DRIVE_DET		; DETERMINE DRIVE
	CALL	XLAT_OLD		; TRANSLATE STATE TO COMPATIBLE MODE
	INC	DI			; POINT TO NEXT DRIVE
	CMP	DI,MAX_DRV		; SEE IF DONE
	JNZ	SUP0			; REPEAT FOR EACH DRIVE
	MOV	@SEEK_STATUS,0		; FORCE RECALIBRATE
	AND	@RTC_WAIT_FLAG,0FEH	; ALLOW FOR RTC WAIT
	CALL	SETUP_END		; VARIOUS CLEANUPS
	POP	DS			; RESTORE CALLERS REGISTERS
	POP	DI
	POP	DX
	POP	CX
	POP	BX
	POP	AX
	RET

DSKETTE_SETUP	ENDP

CODE	ENDS
	END
